DRT modified file to work on any computer
2022-01-17 Some files appeared to be missing to run this notebook.
Trying to get all to run
Overview
This is a pipeline for differential analysis of RNASeq data from
SKMEL5 sublines using DESeq2 statistical package. Three sublines: SC01
(regressing), SC07 (stationary) and SC10
(expanding) were analyzed for gene expression differences. In
addition, time course changes in 8uM PLX4720 were also performed for
each subline. Time points are: 0, 3d, 8d. The differential analysis will
be performed based on the contrasts defined below. General steps for the
analysis are:
###1. Read counts table: + Could be read directly as a csv/txt file.
+ Alignment and read counts could be done within R environment to create
read counts table. 1. Define working directory, load the required
libraries. 2. Get read counts table. Read the raw counts file processed
by featureCounts. The fastq files were aligned with HiSat2, and the read
counts were obtained using featureCounts of Rsubread packages.
pkgs <- c("DESeq2","org.Hs.eg.db","clusterProfiler","HDO.db",
"pheatmap","ggnewscale","PoiClaClu","enrichR","gtable","Rmisc")
source("getReqdPkgs.r")
getReqdPkgs(pkgs)
suppressPackageStartupMessages(expr={
library(plyr)
library(dplyr)
library(ggplot2)
library(ggnewscale)
library(reshape2)
library(DESeq2)
library(ggrepel)
library(pheatmap)
library(org.Hs.eg.db)
library(clusterProfiler)
library("RColorBrewer")
library(enrichR)
})
SAVEFILES <- FALSE
library(biomaRt)
d <- read.csv("../data/featureCounts_matrix_all.csv", header=T, sep=",")
#Rename columns
cols <- c("ensembl_gene_id", "SC01_day0_rep1", "SC01_day0_rep2", "SC01_day0_rep3",
"SC01_day3_rep1", "SC01_day3_rep2", "SC01_day3_rep3",
"SC01_day8_rep1", "SC01_day8_rep2", "SC01_day8_rep3",
"SC07_day0_rep1", "SC07_day0_rep2", "SC07_day0_rep3",
"SC07_day3_rep1", "SC07_day3_rep2", "SC07_day3_rep3",
"SC07_day8_rep1", "SC07_day8_rep2", "SC07_day8_rep3",
"SC10_day0_rep1", "SC10_day0_rep2", "SC10_day0_rep3",
"SC10_day3_rep1", "SC10_day3_rep2", "SC10_day3_rep3",
"SC10_day8_rep1", "SC10_day8_rep2", "SC10_day8_rep3")
names(d) <- cols
ensembl <- useMart("ensembl")
mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
genes <- d$ensembl_gene_id
G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
filters= "ensembl_gene_id",
values=genes,
mart=mart)
GE_data <- merge(d, G_list, by = "ensembl_gene_id")
d <- GE_data[, -1]
d <- d[c(28, seq(1:27))]
rownames(d) <- make.names(d$hgnc_symbol, unique = T)
d <- d[, 2:28]
# remove genes with <5 counts in all samples
d <- d[apply(d, 1, function(x) all(x > 5)),]
countdata <- d
head(countdata)
nrow(countdata)
[1] 14947
ncol(countdata)
[1] 27
Identifying different ion channel gene lists
###2. Convert counts table to DESeq2 object. Convert counts table to
object for DESeq2 or any other analysis pipeline. This step will require
to prepare data object in a form that is suitable for analysis in DESeq2
pipeline: we will need the following to proceed:
- countdata: a table with the read/fragment counts.
- coldata: a table with information about the samples.
Using the matrix of counts and the sample information table, we need
to construct the DESeqDataSet object, for which we will use
DESeqDataSetFromMatrix…..
1. Define the samples and treatment conditions.
condition <- c("0", "3", "8")
treatment <- rep(condition, each=3) # Three biological replicates
unique(treatment)
[1] "0" "3" "8"
cell <- c("SC01", "SC07","SC10") #sublines used for the analysis
cellName <- rep(cell, each=3)
coldata <- data.frame(cell=rep(cellName), treatment=rep(treatment, each=3))
group = factor(paste(coldata$cell, coldata$treatment, sep="."))
coldata$group = group
1. Pre-filtering and normalization.
Pre-filtering and normalization is required to remove lowly expressed
genes.
dds2 <- dds[rowSums(counts(dds)) > 18, ] # remove rows with minimum of 2 read per condition
nrow(dds2)
[1] 14947
# save(dds2, file = "DDS_SC-1,7,10_cell-treat-int.RData")
# load("DDS_SC-1,7,10_cell-treat-int.RData")
2. Visualize sample-to-sample distances.
We could use Principal Component Analysis (PCA) to visualize
relationships between samples.
rld <- rlog(dds2, blind = FALSE)
# save(rld, file = "RLD_SC-1,7,10_0,3,8d_20180701.RData")
# load("RLD_SC-1,7,10_0,3,8d_20180701.RData")
plotPCA(rld, intgroup = c("cell", "treatment"), ntop=5000)

## Use prcomp function
# Colored by cell line, shape by time point, lines connecting time
pca_DEseq <- prcomp(t(assay(rld)))
pca_DEseq_perc <- round(100*pca_DEseq$sdev^2/sum(pca_DEseq$sdev^2),1)
pca_DEseq_df <- data.frame(PC1 = pca_DEseq$x[,1],
PC2 = pca_DEseq$x[,2],
sample = colnames(assay(rld)),
cell.line = rep(c("SC01", "SC07", "SC10"), each = 9),
day = rep(c("Day0", "Day3", "Day8"), each = 3),
replicate = rep(c("Rep1", "Rep2", "Rep3"), times=9))
pca_DEseq_means <- ddply(pca_DEseq_df, .(cell.line, day), summarise, meanPC1 = mean(PC1), meanPC2 = mean(PC2))
ggplot(pca_DEseq_df, aes(PC1,PC2, color = cell.line))+
geom_point(aes(shape = day), size=5) +
geom_path(data = pca_DEseq_means,
aes(x=meanPC1, y=meanPC2,
color=cell.line), arrow = arrow(),
size = 2) +
labs(x=paste0("PC1 (",pca_DEseq_perc[1],"% variance)"), y=paste0("PC2 (",pca_DEseq_perc[2],"% variance)")) +
theme_bw() + ggtitle("PCA - Subclones in Time") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14,
hjust = 0.5,
face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
legend.position = "bottom",
axis.title=element_text(size=12, face="bold"))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

NA
NA
NA
4. Differential Expression Analysis.
Always make sure to use the unnormalized raw counts for this. We will
use DESeq function to perform differential analysis between samples;
Unless specified, the analysis is between the last group and the first
group. Different comparison can be done using ‘contrast’ argument. Steps
involved underneath:
- estimation of size factors (controls for differences in sequencing
depth of the samples)
- estimation of dispersion values for each gene,
- fitting a generalized linear model
1. Running the differential expression pipeline.
design(dds2) = ~ cell + treatment + cell:treatment
dds <- DESeq(dds2, test = "LRT", reduced = ~ cell + treatment)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
# save(dds, file = "DESeq_SC1,7,10_Timecourse_LRT.RData")
# load("DESeq_SC1,7,10_Timecourse_LRT.RData")
# dds
2. Building the results table.
By default, results will extract the estimated log2 fold changes and
p values for the last variable in the design formula. If there are more
than 2 levels for this variable, results will extract the results table
for a comparison of the last level over the first level.
# Esimate the differences between groups by: # a) Lowering the FDR (padj) or (b) raise the log2 fold change.
resultsNames(dds)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
# alpha = FDR adjusted p value cutoff
res <- results(dds, alpha = 0.001)
summary(res)
out of 14947 with nonzero total read count
adjusted p-value < 0.001
LFC > 0 (up) : 3918, 26%
LFC < 0 (down) : 4378, 29%
outliers [1] : 2, 0.013%
low counts [2] : 290, 1.9%
(mean count < 19)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
resOrdered <- res[order(res$pvalue),]
rdata = as.data.frame(res)
Differential expression: days 0 to 8
Change significant log2 fold change to 1.585 (== 3-fold change in
log2 space).
res_0to8d <- results(dds, name="treatment_8_vs_0", cooksCutoff = 0.99,
independentFiltering = TRUE, alpha = 0.05, pAdjustMethod = "BH")
summary(res_0to8d)
out of 14947 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up) : 5581, 37%
LFC < 0 (down) : 5275, 35%
outliers [1] : 23, 0.15%
low counts [2] : 0, 0%
(mean count < 10)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# order results table by the smallest adjusted p value:
res_0to8d <- res_0to8d[order(res_0to8d$padj),]
results_0to8d <- as.data.frame(res_0to8d)
results_0to8d <- mutate(results_0to8d, sig=ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange > 1.585, "Upregulated", ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange < -1.585, "Downregulated", "Not Significant")))
row.names(results_0to8d) <- row.names(res_0to8d)
head(results_0to8d)
DEgenes_0to8d <- results_0to8d[which(abs(results_0to8d$log2FoldChange) > log2(1.5) & results_0to8d$padj < 0.05),]
if(SAVEFILES) write.csv(DEgenes_0to8d, file="~/Desktop/DEgenes_0to8d.csv")
Volcano plot
volcano <- ggplot(results_0to8d, aes(log2FoldChange, -log10(pvalue))) +
geom_point(aes(col = sig)) + theme_bw() +
scale_color_manual(values = c("red", "grey", "green3")) +
# ggtitle("Volcano Plot of Untreated vs Idling") +
labs(x="log2(Fold Change)", y="Log(Odds Ratio)") +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12),
axis.title=element_text(size=12),
legend.position = "none")
volcano

# volcano + ggrepel::geom_text_repel(data=results_0to8d[1:10, ],
# ggplot2::aes(label=rownames(results_0to8d[1:10, ])))
# save(results_0to8d, file="untreatedIdling_DEA.RData")
DEgenes_0to8d <- DEgenes_0to8d[order(abs(DEgenes_0to8d$log2FoldChange),DEgenes_0to8d$sig, decreasing = TRUE),]
temp <- DEgenes_0to8d[DEgenes_0to8d$baseMean > 300,]
temp <- temp[abs(temp$log2FoldChange)>2,]
if(SAVEFILES) write.csv(DEgenes_0to8d, file = "DEgenes_0to8d.csv")
Generating Ion Channel Specific Gene Dataframes
test <- assay(dds)
types <- c("ATP", "TRP", "GABR", "CRACR", "SLC", "KCN", "CACN", "GRI", "ABC", "SCN", "TRP", "RIC3", "CHRND", "RYR")
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- test[grep(paste(types, collapse="|"), rownames(test)),]
test1 <- sapply(samples, function(x) rowMeans(test[, grep(x, colnames(test))]))
rownames(test1) <- rownames(test)
test1 <- test1[order(rownames(test1)),]
test2 <- as.data.frame(test1)
test2["l2FC_SC01_0to8"] <- log2(test2["SC01_day8"]/test2["SC01_day0"])
test2["l2FC_SC07_0to8"] <- log2(test2["SC07_day8"]/test2["SC07_day0"])
test2["l2FC_SC10_0to8"] <- log2(test2["SC10_day8"]/test2["SC10_day0"])
test3 <- subset(test2, l2FC_SC01_0to8 > 1 & l2FC_SC07_0to8 > 1 & l2FC_SC10_0to8 > 1)
test4 <- subset(test2, l2FC_SC01_0to8 > 1 | l2FC_SC07_0to8 > 1 | l2FC_SC10_0to8 > 1)
# write.csv(x = test2, file = "all_ionChannel_Expression.csv")
# write.csv(x = test3, file = "allUpreg_ionChannel_Expression.csv")
# write.csv(x = test4, file = "atLeastOneUpreg_ionChannel_Expression.csv")
test5 <- log2(test4[, 1:9]+1)
pheatmap(test5, cluster_cols = F, cluster_rows = F)

test6 <- log2((test3[,1:9])+1)
pheatmap(test6, cluster_rows = F, cluster_cols = F)

test7 <- test5[rowSums(test5)>30,]
pheatmap(test7, cluster_rows = F, cluster_cols = F)

# load(file="untreatedIdling_DEA.RData")
OrgDB <- org.Hs.eg.db
upreg_genes <- subset(results_0to8d, padj<0.05 & log2FoldChange>2)
downreg_genes <-subset(results_0to8d, padj<0.05 & log2FoldChange<(-2))
geneList_up <- as.vector(upreg_genes$log2FoldChange)
names(geneList_up) <- rownames(upreg_genes)
geneList_down <- as.vector(downreg_genes$log2FoldChange)
names(geneList_down) <- rownames(downreg_genes)
genes_up <- as.vector(rownames(upreg_genes))
genes_down <- as.vector(rownames(downreg_genes))
# names(geneList) <- rownames(results_0to8d)
genes_up_ENTREZID <- bitr(genes_up, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:1 mapping between keys and columns
Warning: 4.56% of input gene IDs are fail to map...
genes_down_ENTREZID <- bitr(genes_down, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:1 mapping between keys and columns
Warning: 6.25% of input gene IDs are fail to map...
# Group GO
ggo_up <- clusterProfiler::groupGO(gene = genes_up_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
level = 3,
readable = TRUE)
ggo_up_df <- as.data.frame(ggo_up)
ggo_up_df <- ggo_up_df[order(-ggo_up_df$Count),]
ggo_down <- clusterProfiler::groupGO(gene = genes_down_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
level = 3,
readable = TRUE)
# View(as.data.frame(ggo_down))
# GO over-representation test
ego_genesUp <- clusterProfiler::enrichGO(gene = genes_up_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
qvalueCutoff = 0.05,
readable = TRUE)
# View(as.data.frame(ego_genesUp))
ego_genesDown <- clusterProfiler::enrichGO(gene = genes_down_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
qvalueCutoff = 0.05,
readable = TRUE)
# View(as.data.frame(ego_genesDown))
# kk_genesUp <- enrichKEGG(gene = genes_up_ENTREZID,
# organism = 'hsa',
# pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesUp))
#
# kk_genesDown <- enrichKEGG(gene = genes_down_ENTREZID,
# organism = 'hsa',
# pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesDown))
# ego_GSEA_up <- gseGO(geneList = geneList_up,
# OrgDb = OrgDB,
# ont = "BP",
# nPerm = 1000,
# minGSSize = 100,
# maxGSSize = 500,
# pvalueCutoff = 0.05,
# verbose = FALSE)
# barplot(ggo_up, order=T)
# barplot(ggo_down)
dotplot(ego_genesUp) + ggtitle("GO Over-representation Upregulated Genes") +
labs(x="Gene Ratio", y="GO Terms") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"))

dotplot(ego_genesDown) + ggtitle("GO Over-representation Downregulated Genes") +
labs(x="Gene Ratio", y="GO Terms") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"))

# emapplot(ego_genesUp)
# emapplot(ego_genesDown)
cnetplot(ego_genesUp, categorySize="pvalue", foldChange=geneList_up)
Warning: Use 'color.params = list(foldChange = your_value)' instead of 'foldChange'.
The foldChange parameter will be removed in the next version.Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

cnetplot(ego_genesDown, categorySize="pvalue", foldChange=geneList_down)
Warning: Use 'color.params = list(foldChange = your_value)' instead of 'foldChange'.
The foldChange parameter will be removed in the next version.Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

ego_genesUp_df <- as.data.frame(ego_genesUp)
egoUp <- ego_genesUp_df[order(-ego_genesUp_df$Count),]
# sorted_egoUp_top10 <- head(egoUp, 10)
egoUp_genes <- strsplit(egoUp$geneID, "/", fixed=TRUE)
# egoUp_top10_genes_all <- unlist(strsplit(head(egoUp, 10)$geneID, "[/]"))
# egoUp_top10_genes_group <- strsplit(sorted_egoUp_top10$geneID, "[/]")
# egoUp_top10_genes_unique <- unique(egoUp_top10_genes)
# table(egoUp_top10_genes)
# egoUp_genesByGroup <- as.data.frame(t(plyr::ldply(egoUp_top10_genes_group, rbind)))
# colnames(egoUp_genesByGroup) <- sorted_egoUp_top10$Description
# egoUp_genesByGroup_ionOnly <- egoUp_genesByGroup[,c(1:6,8:10)]
# write.csv(egoUp_genesByGroup, file="top10GOtermsUpregulated_geneMembership.csv")
# ionGenes <- unique(unlist(egoUp_genesByGroup_ionOnly))
#
# ensembl = useEnsembl(biomart="ensembl", dataset="hsapiens_gene_ensembl")
# IDs <- as.character(ionGenes)
# geneUpID <- names(geneList_up)
# geneDownID <- names(geneList_down)
# genedesc_ion <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = IDs, mart =ensembl)
# write.csv(genedesc_ion, file = "ionChannelGenes_description.csv")
# genedesc_Up <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneUpID, mart =ensembl)
# write.csv(genedesc_Up, file = "upregulatedGenes_description.csv")
# genedesc_Down <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneDownID, mart =ensembl)
# write.csv(genedesc_Down, file = "downrgulatedGenes_description.csv")
geneList_all <- as.vector(results_0to8d$log2FoldChange)
names(geneList_all) <- rownames(results_0to8d)
a <- names(geneList_all)
genes_ENTREZID <- bitr(a, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:many mapping between keys and columns
Warning: 10.72% of input gene IDs are fail to map...
names(geneList_all) <- genes_ENTREZID
gene_df <- data.frame(Entrez=names(geneList_all), HGNC=a, FC=geneList_all)
gene_df <- gene_df[abs(gene_df$FC) > 1,]
gene_df$group <- "upregulated"
gene_df$group[gene_df$FC < 0] <- "downregulated"
gene_df$othergroup <- "A"
gene_df$othergroup[abs(gene_df$FC) > 2] <- "B"
formula_res <- compareCluster(Entrez~group+othergroup, data=gene_df, fun="enrichKEGG")
Warning: No enrichment found in any of gene cluster, please check your input...
head(as.data.frame(formula_res))
NA
3. Exploring Results
plotMA(res, ylim=c(-2,2))

plotCounts(dds, gene=which.min(res$padj), intgroup="treatment")

Log normalize results
# normalizedCounts <- t( t(counts(dds)) / sizeFactors(dds) )
#log2 normalized counts
rld2 <- rlog(dds, blind = FALSE)
# save(rld2, file = "RLD2_SC1,7,10_Timecourse_hmap.RData")
# load("RLD2_SC1,7,10_Timecourse_hmap.RData")
Clustering
sampleDists <- dist(t(assay(rld2)))
sampleDists
SC01_day0_rep1 SC01_day0_rep2 SC01_day0_rep3 SC01_day3_rep1 SC01_day3_rep2 SC01_day3_rep3 SC01_day8_rep1
SC01_day0_rep2 20.65171
SC01_day0_rep3 18.19544 20.23140
SC01_day3_rep1 49.40483 50.31588 48.91851
SC01_day3_rep2 49.71247 50.26344 49.82022 18.63375
SC01_day3_rep3 49.76621 51.27836 49.97240 18.79982 18.39459
SC01_day8_rep1 62.59542 63.01800 62.85874 32.82503 30.94957 31.45619
SC01_day8_rep2 61.72041 62.56061 61.74599 31.23421 30.60196 30.63570 18.73796
SC01_day8_rep3 61.80509 62.21007 61.82013 31.97862 30.72020 31.16024 18.45084
SC07_day0_rep1 81.89472 82.20333 81.04736 88.58433 89.21057 89.31203 92.42884
SC07_day0_rep2 81.81217 82.21052 81.28982 88.75768 89.12405 89.21787 92.00740
SC07_day0_rep3 81.88056 81.87696 80.69148 88.82906 89.71839 89.75197 93.26978
SC07_day3_rep1 85.61380 85.99181 84.75651 72.67150 73.42118 73.65364 73.24722
SC07_day3_rep2 86.14718 85.54792 85.05149 73.05656 73.23269 73.98284 73.12226
SC07_day3_rep3 85.65039 86.08172 85.21571 72.75545 72.78011 73.15645 71.75705
SC07_day8_rep1 80.85774 80.83407 79.81235 68.49272 69.17010 69.63938 69.64941
SC07_day8_rep2 81.78651 81.86365 81.53807 68.99423 68.66122 69.49822 67.68127
SC07_day8_rep3 81.96379 82.97083 82.07809 69.32223 69.04497 69.60840 67.94316
SC10_day0_rep1 83.08483 83.00857 82.09658 91.38963 92.06252 92.15414 95.59004
SC10_day0_rep2 82.31343 82.42280 81.53200 90.68658 91.20490 91.27712 94.48998
SC10_day0_rep3 83.39163 83.06685 81.97586 90.84027 91.32067 91.65829 94.96716
SC10_day3_rep1 94.56538 95.04695 94.07956 78.96682 79.09632 79.16897 77.52321
SC10_day3_rep2 94.54772 94.57592 93.82512 78.91635 79.25387 79.40452 77.96074
SC10_day3_rep3 93.64543 93.44949 92.79417 78.81764 79.42600 79.33981 78.08186
SC10_day8_rep1 87.28001 86.98749 86.05702 69.06667 69.89089 69.80170 65.85874
SC10_day8_rep2 87.47985 86.48166 86.26001 69.46186 69.73044 70.05186 65.80912
SC10_day8_rep3 86.88712 86.54817 85.75162 68.90153 69.55335 69.66973 65.74204
SC01_day8_rep2 SC01_day8_rep3 SC07_day0_rep1 SC07_day0_rep2 SC07_day0_rep3 SC07_day3_rep1 SC07_day3_rep2
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3 18.70173
SC07_day0_rep1 91.45962 91.76440
SC07_day0_rep2 91.40712 91.45839 18.52529
SC07_day0_rep3 92.11601 92.37973 19.01497 20.12043
SC07_day3_rep1 72.19417 72.59749 54.91862 55.10282 54.94104
SC07_day3_rep2 72.53967 72.64952 55.77708 55.88030 56.04933 20.64862
SC07_day3_rep3 71.51446 71.58988 55.65198 55.01198 56.47730 20.64704 21.58226
SC07_day8_rep1 68.65439 68.66252 66.70252 67.10961 66.19657 37.28210 37.68683
SC07_day8_rep2 67.76158 67.38921 68.72697 68.18688 69.23242 39.26255 38.47638
SC07_day8_rep3 67.80631 67.67859 68.79983 68.20694 69.42419 38.93224 39.82064
SC10_day0_rep1 94.47595 94.69824 52.02540 52.36629 51.90081 74.68353 75.29809
SC10_day0_rep2 93.51165 93.71597 51.61810 51.84961 51.64881 74.14492 74.80949
SC10_day0_rep3 93.98204 94.03740 53.25482 53.88450 53.20057 74.39264 74.38328
SC10_day3_rep1 76.83178 77.16526 70.16111 69.88900 70.85687 48.49477 48.88269
SC10_day3_rep2 77.23468 77.40031 70.19613 70.01563 70.35796 48.20663 48.52571
SC10_day3_rep3 77.22063 77.38268 70.35089 70.17405 70.11908 49.54113 50.52511
SC10_day8_rep1 64.53630 64.95505 72.74623 72.51292 72.29412 52.32687 53.63736
SC10_day8_rep2 64.74893 64.98983 72.83182 72.54879 72.38364 53.04593 53.43402
SC10_day8_rep3 64.46140 64.89114 72.30221 72.17670 72.01685 52.43969 53.46083
SC07_day3_rep3 SC07_day8_rep1 SC07_day8_rep2 SC07_day8_rep3 SC10_day0_rep1 SC10_day0_rep2 SC10_day0_rep3
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3
SC07_day0_rep1
SC07_day0_rep2
SC07_day0_rep3
SC07_day3_rep1
SC07_day3_rep2
SC07_day3_rep3
SC07_day8_rep1 38.90824
SC07_day8_rep2 37.57388 23.11737
SC07_day8_rep3 37.48819 23.93251 18.61309
SC10_day0_rep1 75.55978 80.35932 82.91537 83.35237
SC10_day0_rep2 74.65244 79.78211 81.98123 82.22876 17.57831
SC10_day0_rep3 75.28901 79.44072 82.35629 82.91009 22.39798 22.72279
SC10_day3_rep1 47.75866 61.98942 61.90329 61.74289 66.19934 65.55259 66.50108
SC10_day3_rep2 47.95099 61.58948 62.34238 62.54834 65.91055 65.30386 65.89761
SC10_day3_rep3 49.87898 62.12438 63.49194 63.64168 65.62650 65.12926 66.27753
SC10_day8_rep1 52.63151 59.08839 60.79552 60.88443 67.11258 66.51853 67.47053
SC10_day8_rep2 53.03463 59.52660 60.74246 61.37573 67.24014 66.69203 67.60283
SC10_day8_rep3 52.80719 59.01986 60.62481 60.88795 66.48904 65.97280 66.65307
SC10_day3_rep1 SC10_day3_rep2 SC10_day3_rep3 SC10_day8_rep1 SC10_day8_rep2
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3
SC07_day0_rep1
SC07_day0_rep2
SC07_day0_rep3
SC07_day3_rep1
SC07_day3_rep2
SC07_day3_rep3
SC07_day8_rep1
SC07_day8_rep2
SC07_day8_rep3
SC10_day0_rep1
SC10_day0_rep2
SC10_day0_rep3
SC10_day3_rep1
SC10_day3_rep2 18.41568
SC10_day3_rep3 25.81500 23.92748
SC10_day8_rep1 38.31173 36.56576 37.42276
SC10_day8_rep2 39.03488 37.15372 37.99527 19.07979
SC10_day8_rep3 38.31050 36.50575 37.73213 17.66540 18.42072
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste(rld2$treatment, rld2$cell, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
clustering_distance_rows = sampleDists,
clustering_distance_cols = sampleDists,
col = colors)

poisd <- PoiClaClu::PoissonDistance(t(counts(dds)))
samplePoisDistMatrix <- as.matrix( poisd$dd )
rownames(samplePoisDistMatrix) <- paste( dds$dex, dds$cell, sep=" - " )
colnames(samplePoisDistMatrix) <- NULL
pheatmap(samplePoisDistMatrix,
clustering_distance_rows = poisd$dd,
clustering_distance_cols = poisd$dd,
col = colors)

mds <- as.data.frame(colData(rld2)) %>%
cbind(cmdscale(sampleDistMatrix))
ggplot(mds, aes(x = `1`, y = `2`, color = cell, shape = treatment)) +
geom_point(size = 3) + coord_fixed() + theme_bw() +
xlab("PC1") + ylab("PC2") +
theme(legend.text = element_text(size = 10),
plot.title = element_text(size = 14,
hjust = 0.5,
face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
# legend.position = "bottom",
axis.title=element_text(size=12))

# library("genefilter")
topVarGenes <- head(order(rowVars(assay(rld2)), decreasing = TRUE), 5000)
Warning: useNames = NA is deprecated. Instead, specify either useNames = TRUE or useNames = TRUE.
mat <- assay(rld2)[ topVarGenes, ]
mat <- mat - rowMeans(mat)
anno <- as.data.frame(colData(rld2)[, c("cell","treatment")])
names(anno) <- c("Cell", "Treatment")
annotation_colors = list(
Cell = c(SC01="red2", SC07="green2", SC10="blue2"),
Treatment = c("0"="cyan2", "3"="darkorange", "8"="darkorchid"))
pheatmap(mat, annotation_col = anno, show_rownames = F, show_colnames = F,
annotation_colors = annotation_colors)

Time series analysis
1 DESeq2 time series analysis
# browseVignettes("rnaseqGene")
ddsTC <- DESeq(dds, test="LRT", reduced = ~ cell + treatment)
using pre-existing size factors
estimating dispersions
found already estimated dispersions, replacing these
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
resTC <- results(ddsTC)
resTC$symbol <- mcols(ddsTC)$symbol
# head(resTC[order(resTC$padj),], 4)
tc <- plotCounts(ddsTC, which.min(resTC$padj),
intgroup = c("treatment","cell"), returnData = TRUE)
ddsTC[which.min(resTC$padj),]
class: DESeqDataSet
dim: 1 27
metadata(1): version
assays(4): counts mu H cooks
rownames(1): PDK4
rowData names(35): baseMean baseVar ... deviance maxCooks
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(4): cell treatment group sizeFactor
ggplot(tc,
aes(x = rep(c(0,3,8), each=9), y = count, color = cell, group = cell)) +
geom_point() + geom_smooth(se = FALSE, method = "loess") + scale_y_log10() +
theme_bw() +
ggtitle("Time Course Expression of PDK4") +
labs(x="Time (days)", y="Gene Count") +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"),
legend.position = "bottom")

resultsNames(ddsTC)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
betas <- coef(ddsTC)
colnames(betas)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
topGenes <- head(order(resTC$padj),50)
mat <- betas[topGenes, -c(1,2)]
thr <- 3
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
pheatmap(mat, breaks=seq(from=-thr, to=thr, length=101),
cluster_col=FALSE)

NOTE
Original code below produced many messages of
No id variables; using all as measure variables; presumably
a line for each gene. This is due to the melt function not
having any id variables to use.
Rejiggering code not yet finished. Should probably use
2. ANOVA btwn time points & shared btwn sublines)
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC01 <- list()
TukeySC01_time0 <- list()
TukeySC01_time3 <- list()
TukeySC01_time8 <- list()
norm_data_SC01time <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
'SC01_day0_rep2',
'SC01_day0_rep3',
'SC01_day3_rep1',
'SC01_day3_rep2',
'SC01_day3_rep3',
'SC01_day8_rep1',
'SC01_day8_rep2',
'SC01_day8_rep3')]
for (gene in 1:nrow(norm_data_SC01time)) {
gene_norm_data <- norm_data_SC01time[gene,]
# d3 <- data.frame(y = gene_norm_data, group = group)
# fit <- lm(y~group, d3)
# gene_norm_data_melt <- melt(gene_norm_data)
gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
value=as.numeric(t(gene_norm_data)))
gene_norm_data_melt$group <- group
fit <- aov(value~group, gene_norm_data_melt)
# anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
anova_SC01[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
results <- TukeyHSD(fit, conf.level = 0.95)
TukeySC01_time0[gene] <- results$group[,'p adj'][1]
TukeySC01_time3[gene] <- results$group[,'p adj'][2]
TukeySC01_time8[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)
anova_SC01_pval <- unlist(anova_SC01) # make array
TukeySC01_time0_pval <- unlist(TukeySC01_time0)
TukeySC01_time3_pval <- unlist(TukeySC01_time3)
TukeySC01_time8_pval <- unlist(TukeySC01_time8)
# Make master dataset with statistics
norm_data_stats_SC01 <- as.data.frame(norm_data_SC01time)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, anova_SC01_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time0_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time3_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time8_pval)
# save(norm_data_stats_SC01, file = "subcloneCounts_anova_tukey_DESeq2_SC01time.RData")
# Identify genes that differ between clones based on
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC01_pval < sigThresh)
table(TukeySC01_time0_pval < sigThresh)
table(TukeySC01_time3_pval < sigThresh)
table(TukeySC01_time8_pval < sigThresh)
sigIndecies_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh)
sigIndeciesAll_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time0_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time3_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time8_pval"] < sigThresh)
sigDiffGenes_SC01 <- rownames(norm_data_stats_SC01[sigIndecies_SC01,])
sigDiffGenesAll_SC01 <- rownames(norm_data_stats_SC01[sigIndeciesAll_SC01,])
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC07 <- list()
TukeySC07_time0 <- list()
TukeySC07_time3 <- list()
TukeySC07_time8 <- list()
norm_data_SC07time <- as.data.frame(assay(rld2))[c('SC07_day0_rep1',
'SC07_day0_rep2',
'SC07_day0_rep3',
'SC07_day3_rep1',
'SC07_day3_rep2',
'SC07_day3_rep3',
'SC07_day8_rep1',
'SC07_day8_rep2',
'SC07_day8_rep3')]
for (gene in 1:nrow(norm_data_SC07time)) {
gene_norm_data <- norm_data_SC07time[gene,]
# d3 <- data.frame(y = gene_norm_data, group = group)
# fit <- lm(y~group, d3)
# gene_norm_data_melt <- melt(gene_norm_data)
gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
value=as.numeric(t(gene_norm_data)))
gene_norm_data_melt$group <- group
fit <- aov(value~group, gene_norm_data_melt)
# anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
anova_SC07[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
results <- TukeyHSD(fit, conf.level = 0.95)
TukeySC07_time0[gene] <- results$group[,'p adj'][1]
TukeySC07_time3[gene] <- results$group[,'p adj'][2]
TukeySC07_time8[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)
anova_SC07_pval <- unlist(anova_SC07) # make array
TukeySC07_time0_pval <- unlist(TukeySC07_time0)
TukeySC07_time3_pval <- unlist(TukeySC07_time3)
TukeySC07_time8_pval <- unlist(TukeySC07_time8)
# Make master dataset with statistics
norm_data_stats_SC07 <- as.data.frame(norm_data_SC07time)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, anova_SC07_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time0_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time3_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time8_pval)
# save(norm_data_stats_SC07, file = "subcloneCounts_anova_tukey_DESeq2_SC07time.RData")
# Identify genes that differ between clones based on
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC07_pval < sigThresh)
table(TukeySC07_time0_pval < sigThresh)
table(TukeySC07_time3_pval < sigThresh)
table(TukeySC07_time8_pval < sigThresh)
sigIndecies_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh)
sigIndeciesAll_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time0_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time3_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time8_pval"] < sigThresh)
sigDiffGenes_SC07 <- rownames(norm_data_stats_SC07[sigIndecies_SC07,])
sigDiffGenesAll_SC07 <- rownames(norm_data_stats_SC07[sigIndeciesAll_SC07,])
3 Jack’s method
#Grab all the names from res in the DESeq matrix
topGenes <- which(res$padj <= 0.001)
countMAT <- data.frame(normalizedCounts[topGenes,])
subrl = data.frame(assay(rld2))
rlMAT = data.frame(subrl[topGenes,])
#Labeling rows with ENSG IDs
# countMAT$ensembl_gene_id = row.names(countMAT)
# countMAT$padj = res[topGenes,"padj"]
rlMAT$ensembl_gene_id = row.names(rlMAT)
rlMAT$padj = res[topGenes,"padj"]
# library(biomaRt)
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(rlMAT)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
# filters= "ensembl_gene_id",
# values=genes,
# mart=mart)
#Check if data fits a normal distribution
# plot(density(c(as.matrix(countMAT[,1:27]))))
plot(density(c(as.matrix(rlMAT[,1:27]))))
#rlMAT follows a normal distribution, therefore we will use this in the heatmap construction
#Labeling df with hgnc symbols
GE_data <- merge(G_list, rlMAT, by = "ensembl_gene_id")
#Making rownames unique hgnc symbols
rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
GE_data = GE_data[order(GE_data$padj),]
#Averaging rld between trials
Acol <- c("SC01_day0",
"SC01_day3",
"SC01_day8",
"SC07_day0",
"SC07_day3",
"SC07_day8",
"SC10_day0",
"SC10_day3",
"SC10_day8")
for(i in 1:length(Acol)){
j = 2+i
k = 2+3*i
GE_data[,Acol[i]] = rowMeans(GE_data[,c(j:k)])
}
#Calculating fold changes across conditions in a triangular matrix form
GE_mean = GE_data[,c(1,2,30:39)]
DEProc = GE_mean
startcol = 4
endcol = 12
allFC <- function(DEProc,startcol,endcol){
GE_fold = DEProc[,-c(startcol:endcol)]
colvec = colnames(DEProc)[startcol:endcol]
#Last index is a self comparison and is removed
for(k in 1:(length(colvec)-1)){
#Start with column that is 1 away from index
for(j in (k+1):length(colvec)){
compnam = paste0(colvec[j],"/",colvec[k])
#Loop through each gene/row
for(i in 1:nrow(DEProc)){
f = DEProc[i,colvec[j]]
h = DEProc[i,colvec[k]]
#Capture upregulation and down regulation
if(f>h){
GE_fold[i,compnam] = 2^(f-h)
}else{
GE_fold[i,compnam] = -2^(h-f)
}
}
}
}
return(GE_fold)
}
#Subset gene, then plot, then save plot
#Perhaps make heatmaps with scaled z scores
#Is there a way to consolidate replicate z scores? Geometric mean?
#Regular mean, then scale.
# ImpRat = colnames(GE_fold)[c(4,5,6,9,12,14,17,21,24,25,26,27,30,32,36,37,38,39)]
#Listing of all important comparisons?
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day3", "SC01_day8/SC01_day0",
"SC07_day3/SC07_day0", "SC07_day8/SC07_day3", "SC07_day8/SC07_day0",
"SC10_day3/SC10_day0", "SC10_day8/SC10_day3", "SC10_day8/SC10_day0",
"SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0",
"SC07_day3/SC01_day3", "SC10_day3/SC01_day3", "SC10_day3/SC07_day3",
"SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8" )
Imp_fold = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", ImpRat)]
Imp_fold2 = Imp_fold[rowSums(abs(Imp_fold[,4:21])>=1.5)>=1,]
# write.table(Imp_fold,"SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t", row.names=F)
Imp_fold = read.delim("SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t")
#Subset the LF mean of important genes from Log2 Fold Change (LFC) comparison data frame.
GE_Imp = subset(GE_mean,GE_mean$ensembl_gene_id%in%Imp_fold2$ensembl_gene_id)
Necro = read.delim("KEGGNecroptosis_hsa04217_06-25-18.txt", header=T, stringsAsFactors = F)
Necro = Necro[rowSums(is.na(Necro)) == 0, ]
DE_Necro = merge(GE_Imp, Necro, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Necro) = make.names(DE_Necro[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Necro[3:29],cluster_cols = TRUE)
# write.table(DE_Necro, "KEGGNecroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
Apop = read.delim("KEGGApoptosis_hsa04210_06-25-18.txt", header=T, stringsAsFactors = F)
Apop = Apop[rowSums(is.na(Apop)) == 0, ]
DE_Apop = merge(GE_Imp), Apop, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Apop) = make.names(DE_Apop[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Apop[3:29],cluster_cols = TRUE, scale = "row")
# write.table(DE_Apop, "KEGGApoptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
Ferr = read.delim("KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr = Ferr[rowSums(is.na(Ferr)) == 0, ]
DE_Ferr = merge(GE_Imp, Ferr, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Ferr) = make.names(DE_Ferr[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Ferr[4:12],cluster_cols=FALSE, scale = "row")
# write.table(DE_Ferr, "KEGGFerroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
4. Different LC comparisons. Between subclones and at baseline vs
idling.
Zscore heatmaps of relevant comparisons can be made as in above to
visualize.
#USES ABOVE CODE TO LINE 280. Run that pseudo-function.
# library(pheatmap)
#Comparisons of difEx between subclones at baseline and idling
BetweenBase = c("SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0")
BetweenIdle = c("SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8")
#Unsure of how strict to make the cutoff. Should all the genes between clones be differentially expressed (3) or is a single difference sufficient?
Btw_b = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenBase)]
Btw_b1 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=1,]
Btw_b2 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=2,]
Btw_b3 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=3,]
Btw_i = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenIdle)]
Btw_i1 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=1,]
Btw_i2 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=2,]
Btw_i3 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=3,]
#This does not account for same direction of change. This can be plotted in a heatmap to view.
#Members that were "lost" by the baseline condition at being different. Were no longer found diffEx between the subclones when comparing baseline to idling DEGs.
LostDEG_b_1 = subset(Btw_b1,!Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
LostDEG_b_2 = subset(Btw_b2,!Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
LostDEG_b_3 = subset(Btw_b3, !Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)
##Make heatmap
LostDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_3$ensembl_gene_id)
row.names(LostDEG_b_3_mean) = make.names(LostDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that remained different.
StaticDEG_b_1 = subset(Btw_b1,Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
StaticDEG_b_2 = subset(Btw_b2,Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
StaticDEG_b_3 = subset(Btw_b3, Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)
##Some HGNC_symbols are duplicates! I switched to ensembl_gene_id to fix.
StaticDEG_i_3 = subset(Btw_i3, Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)
##Make heatmap
StaticDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%StaticDEG_b_3$ensembl_gene_id)
row.names(StaticDEG_b_3_mean) = make.names(StaticDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(StaticDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that "gained" differences between the subclones in idling.
GainDEG_i_1 = subset(Btw_i1, !Btw_i1$ensembl_gene_id%in%Btw_b1$ensembl_gene_id)
GainDEG_i_2 = subset(Btw_i2, !Btw_i2$ensembl_gene_id%in%Btw_b2$ensembl_gene_id)
GainDEG_i_3 = subset(Btw_i3, !Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)
##Make heatmap
GainDEG_i_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%GainDEG_i_3$ensembl_gene_id)
row.names(GainDEG_i_3_mean) = make.names(GainDEG_i_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(GainDEG_i_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that were differentially expressed between idling (8day) and baseline within subclones. Those with shared diffEx may be convergent across multiple subclones depending on direction of expresison change.
Endpoint = c("SC01_day8/SC01_day0", "SC07_day8/SC07_day0", "SC10_day8/SC10_day0")
BtoIdle = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", Endpoint)]
BtoIdle_1 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=1,]
BtoIdle_2 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=2,]
BtoIdle_3 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=3,]
##Make heatmap
BtoIdle_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%BtoIdle_2$ensembl_gene_id)
row.names(BtoIdle_2_mean) = make.names(BtoIdle_2_mean[,"hgnc_symbol"], unique = TRUE)
BtoIdle_2_mean_incExp = BtoIdle_2_mean[which(BtoIdle_2_mean$SC01_day0 < BtoIdle_2_mean$SC01_day8),]
BtoIdle_2_mean_incExp = BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC07_day0 < BtoIdle_2_mean_incExp$SC07_day8),]
BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC10_day0 < BtoIdle_2_mean_incExp$SC10_day8),]
LostDEG_b_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_2$ensembl_gene_id)
row.names(LostDEG_b_2_mean) = make.names(LostDEG_b_2_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_2_mean[4:12],cluster_cols=FALSE, scale = "row")
BtoIdleIncExp_DEbetweenSCs = BtoIdle_2_mean_incExp[which(row.names(BtoIdle_2_mean_incExp) %in% row.names(LostDEG_b_2_mean)),]
pheatmap(BtoIdle_2_mean_incExp[4:12],cluster_cols=FALSE, scale = "row")
# library(devtools)
# # install_github("wjawaid/enrichR")
# library(enrichR)
dbs <- listEnrichrDbs()
head(dbs)
dbs <- c("GO_Molecular_Function_2015", "GO_Cellular_Component_2015", "GO_Biological_Process_2015")
enriched <- enrichr(row.names(BtoIdle_2_mean_incExp), dbs)
View(enriched[["GO_Molecular_Function_2015"]])
View(enriched[["GO_Cellular_Component_2015"]])
View(enriched[["GO_Biological_Process_2015"]])
enriched_MF_sig <- enriched[["GO_Molecular_Function_2015"]][enriched[["GO_Molecular_Function_2015"]]$Adjusted.P.value<0.05,]
enriched_MF_sig_df <- data.frame(enriched_MF_sig[,c(1,4,9)])
write.csv(enriched_MF_sig_df, "enriched_MF_significant.csv")
enriched_BP_sig <- enriched[["GO_Biological_Process_2015"]][enriched[["GO_Biological_Process_2015"]]$Adjusted.P.value<0.05,]
enriched_BP_sig_df <- data.frame(enriched_BP_sig[,c(1,4,9)])
# write.csv(enriched_BP_sig_df, "enriched_BP_significant.csv")
Gini_scGenes <- c("APOE", "BCAN", "CES1", "CITED1",
"CPM", "CTSF", "DCT", "EDNRB",
"EGR1", "ESRP1", "FSTL1", "MALAT1",
"MAP2K6", "MCF2L", "MLANA", "MXD4",
"OCA2", "PMEL", "SEMA6A", "SNAI2",
"SOX4", "TSPAN10")
enriched_sc <- enrichr(Gini_scGenes, dbs)
row.names(BtoIdle_2_mean_incExp) %in% Gini_scGenes
Rest of Jack’s Analysis
#Visually inspect trending members from heatmaps.
#Plots of specific trending members?
p <- ggplot(data=df2, aes(x=dose, y=len, fill=supp)) +
geom_bar(stat="identity", color="black", position=position_dodge())+
theme_minimal()
NOTE: code below reuses object names… WILL OVERWRITE!
#GLM Coef Heatmap.
betas <- coef(dds)
topGenes <- which(res$padj <= 0.001)
mat <- data.frame(betas[topGenes,])
mat$ensembl_gene_id = row.names(mat)
mat$padj = res[topGenes,"padj"]
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(mat)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
# filters= "ensembl_gene_id",
# values=genes,
# mart=mart)
# GE_data <- merge(mat, G_list, by = "ensembl_gene_id")
# rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
# GE_data = GE_data[order(GE_data$padj),]
#Sorting script to pick out entries greater than or less than +-1
eg = c()
for(i in 3:10){
g = which(GE_data[,i] > 3 | GE_data[,i] < -3)
eg = c(eg,g)
}
eg = unique(eg)
mat = GE_data[eg,-c(1:2,11,12)]
thr <- 3
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
# library(pheatmap)
pheatmap(mat, cluster_cols = FALSE)
# ssdg = sdg[1:1000, ]
dim(sdg)
head(sdg)
LS0tCnRpdGxlOiAiUk5Bc2VxIERFU2VxMiBUaW1lIENvdXJzZSIKYXV0aG9yOiAiQ29yZXkgSGF5Zm9yZCAobW9kaWZlZCBmcm9tIEphY2spIgpkYXRlOiAiTm92ZW1iZXIgMjAxOC1KYW51YXJ5IDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBEUlQgbW9kaWZpZWQgZmlsZSB0byB3b3JrIG9uIGFueSBjb21wdXRlcgoyMDIyLTAxLTE3ClNvbWUgZmlsZXMgYXBwZWFyZWQgdG8gYmUgbWlzc2luZyB0byBydW4gdGhpcyBub3RlYm9vay4gVHJ5aW5nIHRvIGdldCBhbGwgdG8gcnVuCgojIyBPdmVydmlldwpUaGlzIGlzIGEgcGlwZWxpbmUgZm9yIGRpZmZlcmVudGlhbCBhbmFseXNpcyBvZiBSTkFTZXEgZGF0YSBmcm9tIFNLTUVMNSBzdWJsaW5lcyB1c2luZyBERVNlcTIgc3RhdGlzdGljYWwgcGFja2FnZS4gVGhyZWUgc3VibGluZXM6IFNDMDEgKCpyZWdyZXNzaW5nKiksIFNDMDcgKCpzdGF0aW9uYXJ5KikgYW5kIFNDMTAgKCpleHBhbmRpbmcqKSB3ZXJlIGFuYWx5emVkIGZvciBnZW5lIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMuIEluIGFkZGl0aW9uLCB0aW1lIGNvdXJzZSBjaGFuZ2VzIGluIDh1TSBQTFg0NzIwIHdlcmUgYWxzbyBwZXJmb3JtZWQgZm9yIGVhY2ggc3VibGluZS4gVGltZSBwb2ludHMgYXJlOiAwLCAzZCwgOGQuIFRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgd2lsbCBiZSBwZXJmb3JtZWQgYmFzZWQgb24gdGhlIGNvbnRyYXN0cyBkZWZpbmVkIGJlbG93LiAKR2VuZXJhbCBzdGVwcyBmb3IgdGhlIGFuYWx5c2lzIGFyZToKICAKIyMjMS4gUmVhZCBjb3VudHMgdGFibGU6IAorIENvdWxkIGJlIHJlYWQgZGlyZWN0bHkgYXMgYSBjc3YvdHh0IGZpbGUuIAorIEFsaWdubWVudCBhbmQgcmVhZCBjb3VudHMgY291bGQgYmUgZG9uZSB3aXRoaW4gUiBlbnZpcm9ubWVudCB0byBjcmVhdGUgcmVhZCBjb3VudHMgdGFibGUuIAoxLiBEZWZpbmUgd29ya2luZyBkaXJlY3RvcnksIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcy4gCjIuIEdldCByZWFkIGNvdW50cyB0YWJsZS4gClJlYWQgdGhlIHJhdyBjb3VudHMgZmlsZSBwcm9jZXNzZWQgYnkgZmVhdHVyZUNvdW50cy4gVGhlIGZhc3RxIGZpbGVzIHdlcmUgYWxpZ25lZCB3aXRoIEhpU2F0MiwgYW5kIHRoZSByZWFkIGNvdW50cyB3ZXJlIG9idGFpbmVkIHVzaW5nIGZlYXR1cmVDb3VudHMgb2YgUnN1YnJlYWQgcGFja2FnZXMuCgpgYGB7ciBJbnN0YWxsYXRpb24sIGV2YWw9RkFMU0V9CnBrZ3MgPC0gYygiREVTZXEyIiwib3JnLkhzLmVnLmRiIiwiY2x1c3RlclByb2ZpbGVyIiwiSERPLmRiIiwKICAgICAgICAgICJwaGVhdG1hcCIsImdnbmV3c2NhbGUiLCJQb2lDbGFDbHUiLCJlbnJpY2hSIiwiZ3RhYmxlIiwiUm1pc2MiKQpzb3VyY2UoImdldFJlcWRQa2dzLnIiKQpnZXRSZXFkUGtncyhwa2dzKQpgYGAKCgpgYGB7ciBTZXR1cH0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGV4cHI9ewogICAgbGlicmFyeShwbHlyKQogICAgbGlicmFyeShkcGx5cikKICAgIGxpYnJhcnkoZ2dwbG90MikKICAgIGxpYnJhcnkoZ2duZXdzY2FsZSkKICAgIGxpYnJhcnkocmVzaGFwZTIpCiAgICBsaWJyYXJ5KERFU2VxMikKICAgIGxpYnJhcnkoZ2dyZXBlbCkKICAgIGxpYnJhcnkocGhlYXRtYXApCiAgICBsaWJyYXJ5KG9yZy5Icy5lZy5kYikKICAgIGxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQogICAgbGlicmFyeSgiUkNvbG9yQnJld2VyIikKICAgIGxpYnJhcnkoZW5yaWNoUikKfSkKClNBVkVGSUxFUyA8LSBGQUxTRQpgYGAKCmBgYHtyLCBlY2hvPVRSVUV9CmxpYnJhcnkoYmlvbWFSdCkKZCA8LSByZWFkLmNzdigiLi4vZGF0YS9mZWF0dXJlQ291bnRzX21hdHJpeF9hbGwuY3N2IiwgaGVhZGVyPVQsIHNlcD0iLCIpCgojUmVuYW1lIGNvbHVtbnMKY29scyA8LSBjKCJlbnNlbWJsX2dlbmVfaWQiLCAiU0MwMV9kYXkwX3JlcDEiLCAiU0MwMV9kYXkwX3JlcDIiLCAiU0MwMV9kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMDFfZGF5M19yZXAxIiwgIlNDMDFfZGF5M19yZXAyIiwgIlNDMDFfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzAxX2RheThfcmVwMSIsICJTQzAxX2RheThfcmVwMiIsICJTQzAxX2RheThfcmVwMyIsCiAgICAgICAgICAiU0MwN19kYXkwX3JlcDEiLCAiU0MwN19kYXkwX3JlcDIiLCAiU0MwN19kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMDdfZGF5M19yZXAxIiwgIlNDMDdfZGF5M19yZXAyIiwgIlNDMDdfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzA3X2RheThfcmVwMSIsICJTQzA3X2RheThfcmVwMiIsICJTQzA3X2RheThfcmVwMyIsCiAgICAgICAgICAiU0MxMF9kYXkwX3JlcDEiLCAiU0MxMF9kYXkwX3JlcDIiLCAiU0MxMF9kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMTBfZGF5M19yZXAxIiwgIlNDMTBfZGF5M19yZXAyIiwgIlNDMTBfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzEwX2RheThfcmVwMSIsICJTQzEwX2RheThfcmVwMiIsICJTQzEwX2RheThfcmVwMyIpCm5hbWVzKGQpIDwtIGNvbHMKZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKZ2VuZXMgPC0gZCRlbnNlbWJsX2dlbmVfaWQKR19saXN0IDwtIGdldEJNKGF0dHJpYnV0ZXM9IGMoImVuc2VtYmxfZ2VuZV9pZCIsImhnbmNfc3ltYm9sIiksCiAgICAgICAgICAgICAgICBmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwKICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKICAgICAgICAgICAgICAgIG1hcnQ9bWFydCkKCkdFX2RhdGEgPC0gbWVyZ2UoZCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQpkIDwtIEdFX2RhdGFbLCAtMV0KZCA8LSBkW2MoMjgsIHNlcSgxOjI3KSldCnJvd25hbWVzKGQpIDwtIG1ha2UubmFtZXMoZCRoZ25jX3N5bWJvbCwgdW5pcXVlID0gVCkKZCA8LSBkWywgMjoyOF0KCiMgcmVtb3ZlIGdlbmVzIHdpdGggPDUgY291bnRzIGluIGFsbCBzYW1wbGVzCmQgPC0gZFthcHBseShkLCAxLCBmdW5jdGlvbih4KSBhbGwoeCA+IDUpKSxdCgoKY291bnRkYXRhIDwtIGQKCmhlYWQoY291bnRkYXRhKQpucm93KGNvdW50ZGF0YSkKbmNvbChjb3VudGRhdGEpCmBgYAoKIyMjIElkZW50aWZ5aW5nIGRpZmZlcmVudCBpb24gY2hhbm5lbCBnZW5lIGxpc3RzCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmNvdW50ZGF0YV9oaXN0YW1pbmUgPSBjb3VudGRhdGFbYygiSFJIMSIsIkhSSDIiLCJIUkgzIiwiSFJINCIpLF0KY291bnRkYXRhX0lQcyA9IGNvdW50ZGF0YVtjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpLF0KY291bnRkYXRhX0lQcmVsID0gY291bnRkYXRhW2MoIklUUFIxIiwgIklUUFIyIiwgIklUUFIzIiwgIlBMQ0cxIiwiREdLQSIsICJER0tCIiwgIkRHS0QiLCAiREdLRSIsICJER0tHIiwgIkRHS0giLCAiREdLSSIsICJER0tLIiwgIkRHS1EiLCAiREdLWiIsICJQSUszQ0EiLCAiUElLM0NCIiwgIlBJSzNDRCIsICJQSUszQ0ciLCAiUElLM0MyQSIsICJQSUszQzJCIiwgIlBJSzNDMkciLCAiUElLM0MzIiksXQpjb3VudGRhdGFfbXVzYyA9IGNvdW50ZGF0YVtjKCJDSFJNMSIsICJDSFJNMiIsICJDSFJNMyIsICJDSFJNNCIsICJDSFJNNSIpLF0KY291bnRkYXRhX2dsdXQgPSBjb3VudGRhdGFbYygiR1JNMSIsICJHUk0yIiwgIkdSTTMiLCAiR1JNNCIsICJHUk01IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JNNiIsICJHUk03IiwgIkdSTTgiLCAiR1JJQTEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JJQTIiLCAiR1JJQTMiLCAiR1JJQTQiLCAiR1JJRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklEMiIsICJHUklLMSIsICJHUklLMiIsICJHUklLMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklLNCIsICJHUklLNSIsICJHUklOMSIsICJHUklOMkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOMkIiLCAiR1JJTjJDIiwgIkdSSU4yRCIsICJHUklOM0EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOM0IiKSxdCgojIyMgUGxvdCBJUCMgZGF0YQojIHNvdXJjZSgic3VtbWFyeVNFLlIiKQpJUHNfbWF0Y2ggPSBjb2xzcGxpdChjb2xuYW1lcyhjb3VudGRhdGFfSVBzKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCklQc19wbG90ID0gY2JpbmQoSVBzX21hdGNoLCB0KGNvdW50ZGF0YV9JUHMpKQpJUHNfbWVsdCA9IG1lbHQoZGF0YSA9IElQc19wbG90LCBpZC52YXJzID0gYygiUG9wdWxhdGlvbiIsICJUaW1lIiwgIlJlcGxpY2F0ZSIpLCBtZWFzdXJlLnZhcnMgPSBjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpKQpJUHNfZGF0ID0gUk1pc2M6OnN1bW1hcnlTRShJUHNfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpJUHNfZGF0JFRpbWUgPSBhcy5udW1lcmljKGdzdWIoIlteWzpkaWdpdDpdXSIsIiIsSVBzX2RhdCRUaW1lKSkKSVBzX2RhdF9zdWIgPSBzdWJzZXQoSVBzX2RhdCwgdmFyaWFibGUgJWluJSBjKCJJVFBSMiIsIklUUFIzIikpCklQc19nZ3Bsb3RlZCA8LSBnZ3Bsb3QoSVBzX2RhdF9zdWIsIGFlcyh4PVRpbWUsIHk9dmFsdWUsIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyBnZW9tX2xpbmUoc2l6ZT0xLjUsIGFlcyhjb2xvciA9IFBvcHVsYXRpb24sIGxpbmV0eXBlID0gdmFyaWFibGUpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZS1zZCwgeW1heD12YWx1ZStzZCwgY29sb3IgPSBQb3B1bGF0aW9uKSwgd2lkdGg9LjIsIHNpemU9MS41KSArCnRoZW1lX2J3KCkgKyB4bGFiKCJUaW1lIChkYXlzKSIpICsgeWxhYigiR2VuZSBDb3VudHMiKSArCmdndGl0bGUoIklQMyBnZW5lIHJlY2VwdG9ycyIpICsKdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLAogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCklQc19nZ3Bsb3RlZAojIGdnc2F2ZSgiSVAzUjJfM19UaW1lU2VyaWVzUk5Bc2VxX2Nsb25lcy5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKIyMjMi4gQ29udmVydCBjb3VudHMgdGFibGUgdG8gREVTZXEyIG9iamVjdC4gCkNvbnZlcnQgY291bnRzIHRhYmxlIHRvIG9iamVjdCBmb3IgREVTZXEyIG9yIGFueSBvdGhlciBhbmFseXNpcyBwaXBlbGluZS4gVGhpcyBzdGVwIHdpbGwgcmVxdWlyZSB0byBwcmVwYXJlIGRhdGEgb2JqZWN0IGluIGEgZm9ybSB0aGF0IGlzIHN1aXRhYmxlIGZvciBhbmFseXNpcyBpbiBERVNlcTIgcGlwZWxpbmU6IHdlIHdpbGwgbmVlZCB0aGUgZm9sbG93aW5nIHRvIHByb2NlZWQ6CiAgCiAgKyBjb3VudGRhdGE6IGEgdGFibGUgd2l0aCB0aGUgcmVhZC9mcmFnbWVudCBjb3VudHMuIAorIGNvbGRhdGE6IGEgdGFibGUgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcy4gCgpVc2luZyB0aGUgbWF0cml4IG9mIGNvdW50cyBhbmQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZSwgd2UgbmVlZCB0byBjb25zdHJ1Y3QgdGhlIERFU2VxRGF0YVNldCBvYmplY3QsIGZvciB3aGljaCB3ZSB3aWxsIHVzZSBERVNlcURhdGFTZXRGcm9tTWF0cml4Li4uLi4KCiMjIyMgMS4gRGVmaW5lIHRoZSBzYW1wbGVzIGFuZCB0cmVhdG1lbnQgY29uZGl0aW9ucy4gCmBgYHtyfQpjb25kaXRpb24gPC0gYygiMCIsICIzIiwgIjgiKQp0cmVhdG1lbnQgPC0gcmVwKGNvbmRpdGlvbiwgZWFjaD0zKSAjIFRocmVlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcwp1bmlxdWUodHJlYXRtZW50KQpjZWxsIDwtIGMoIlNDMDEiLCAiU0MwNyIsIlNDMTAiKSAjc3VibGluZXMgdXNlZCBmb3IgdGhlIGFuYWx5c2lzCmNlbGxOYW1lIDwtIHJlcChjZWxsLCBlYWNoPTMpCgpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoY2VsbD1yZXAoY2VsbE5hbWUpLCB0cmVhdG1lbnQ9cmVwKHRyZWF0bWVudCwgZWFjaD0zKSkKZ3JvdXAgPSBmYWN0b3IocGFzdGUoY29sZGF0YSRjZWxsLCBjb2xkYXRhJHRyZWF0bWVudCwgc2VwPSIuIikpCmNvbGRhdGEkZ3JvdXAgPSBncm91cApgYGAKCiMjIyMgMi4gY29uc3RydWN0IHRoZSBERVNlcURhdGFTZXQgb2JqZWN0IGZyb20gdGhlIG1hdHJpeCBvZiBjb3VudHMgYW5kIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUuIApEZXNjcmliZWQgYWJvdmUgYXJlOiBjb3VudGRhdGEtIHJhdyBjb3VudHMsIGNvbGRhdGE6IHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZS4gCmBgYHtyfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGNlbGwgKyB0cmVhdG1lbnQgKyBjZWxsOnRyZWF0bWVudCkKZGRzCm5yb3coZGRzKTsgbmNvbChkZHMpCmBgYAoKIyMjMy4gRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uClRoZXJlIGFyZSB0d28gc2VwYXJhdGUgc3RlcHMgaW4gdGhlIHdvcmtmbG93OyB0aGUgb25lIHdoaWNoIGludm9sdmVzIGRhdGEgdHJhbnNmb3JtYXRpb25zIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSBzYW1wbGUgcmVsYXRpb25zaGlwcyBhbmQgdGhlIHNlY29uZCBzdGVwIGludm9sdmVzIHN0YXRpc3RpY2FsIHRlc3RpbmcgbWV0aG9kcyB3aGljaCByZXF1aXJlcyB0aGUgb3JpZ2luYWwgcmF3IGNvdW50cy4gCgojIyMjIDEuIFByZS1maWx0ZXJpbmcgYW5kIG5vcm1hbGl6YXRpb24uIApQcmUtZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uIGlzIHJlcXVpcmVkIHRvIHJlbW92ZSBsb3dseSBleHByZXNzZWQgZ2VuZXMuIAoKYGBge3J9CmRkczIgPC0gZGRzW3Jvd1N1bXMoY291bnRzKGRkcykpID4gMTgsIF0gIyByZW1vdmUgcm93cyB3aXRoIG1pbmltdW0gb2YgMiByZWFkIHBlciBjb25kaXRpb24KCm5yb3coZGRzMikKIyBzYXZlKGRkczIsIGZpbGUgPSAiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCiMgbG9hZCgiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCgpgYGAKCiMjIyMgMi4gVmlzdWFsaXplIHNhbXBsZS10by1zYW1wbGUgZGlzdGFuY2VzLiAKV2UgY291bGQgdXNlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgdG8gdmlzdWFsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAKYGBge3J9CnJsZCA8LSBybG9nKGRkczIsIGJsaW5kID0gRkFMU0UpCiMgc2F2ZShybGQsIGZpbGUgPSAiUkxEX1NDLTEsNywxMF8wLDMsOGRfMjAxODA3MDEuUkRhdGEiKQojIGxvYWQoIlJMRF9TQy0xLDcsMTBfMCwzLDhkXzIwMTgwNzAxLlJEYXRhIikKcGxvdFBDQShybGQsIGludGdyb3VwID0gYygiY2VsbCIsICJ0cmVhdG1lbnQiKSwgbnRvcD01MDAwKQoKIyMgVXNlIHByY29tcCBmdW5jdGlvbgojIENvbG9yZWQgYnkgY2VsbCBsaW5lLCBzaGFwZSBieSB0aW1lIHBvaW50LCBsaW5lcyBjb25uZWN0aW5nIHRpbWUKcGNhX0RFc2VxIDwtIHByY29tcCh0KGFzc2F5KHJsZCkpKQpwY2FfREVzZXFfcGVyYyA8LSByb3VuZCgxMDAqcGNhX0RFc2VxJHNkZXZeMi9zdW0ocGNhX0RFc2VxJHNkZXZeMiksMSkKcGNhX0RFc2VxX2RmIDwtIGRhdGEuZnJhbWUoUEMxID0gcGNhX0RFc2VxJHhbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgUEMyID0gcGNhX0RFc2VxJHhbLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID0gY29sbmFtZXMoYXNzYXkocmxkKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGwubGluZSA9IHJlcChjKCJTQzAxIiwgIlNDMDciLCAiU0MxMCIpLCBlYWNoID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHJlcChjKCJEYXkwIiwgIkRheTMiLCAiRGF5OCIpLCBlYWNoID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpY2F0ZSA9IHJlcChjKCJSZXAxIiwgIlJlcDIiLCAiUmVwMyIpLCB0aW1lcz05KSkKCnBjYV9ERXNlcV9tZWFucyA8LSBkZHBseShwY2FfREVzZXFfZGYsIC4oY2VsbC5saW5lLCBkYXkpLCBzdW1tYXJpc2UsIG1lYW5QQzEgPSBtZWFuKFBDMSksIG1lYW5QQzIgPSBtZWFuKFBDMikpCgpnZ3Bsb3QocGNhX0RFc2VxX2RmLCBhZXMoUEMxLFBDMiwgY29sb3IgPSBjZWxsLmxpbmUpKSsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRheSksIHNpemU9NSkgKwogIGdlb21fcGF0aChkYXRhID0gcGNhX0RFc2VxX21lYW5zLCAKICAgICAgICAgICAgYWVzKHg9bWVhblBDMSwgeT1tZWFuUEMyLAogICAgICAgICAgICAgICAgY29sb3I9Y2VsbC5saW5lKSwgYXJyb3cgPSBhcnJvdygpLAogICAgICAgICAgICBzaXplID0gMikgKwogIGxhYnMoeD1wYXN0ZTAoIlBDMSAoIixwY2FfREVzZXFfcGVyY1sxXSwiJSB2YXJpYW5jZSkiKSwgeT1wYXN0ZTAoIlBDMiAoIixwY2FfREVzZXFfcGVyY1syXSwiJSB2YXJpYW5jZSkiKSkgKwogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQQ0EgLSBTdWJjbG9uZXMgaW4gVGltZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQogIAoKCmBgYAoKIyMjIDQuIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzLiAKQWx3YXlzIG1ha2Ugc3VyZSB0byB1c2UgdGhlIHVubm9ybWFsaXplZCByYXcgY291bnRzIGZvciB0aGlzLiBXZSB3aWxsIHVzZSBERVNlcSBmdW5jdGlvbiB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBhbmFseXNpcyBiZXR3ZWVuIHNhbXBsZXM7IFVubGVzcyBzcGVjaWZpZWQsIHRoZSBhbmFseXNpcyBpcyBiZXR3ZWVuIHRoZSBsYXN0IGdyb3VwIGFuZCB0aGUgZmlyc3QgZ3JvdXAuIERpZmZlcmVudCBjb21wYXJpc29uIGNhbiBiZSBkb25lIHVzaW5nICdjb250cmFzdCcgYXJndW1lbnQuIFN0ZXBzIGludm9sdmVkIHVuZGVybmVhdGg6CiAgCjEuIGVzdGltYXRpb24gb2Ygc2l6ZSBmYWN0b3JzIChjb250cm9scyBmb3IgZGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCBvZiB0aGUgc2FtcGxlcykKMi4gZXN0aW1hdGlvbiBvZiBkaXNwZXJzaW9uIHZhbHVlcyBmb3IgZWFjaCBnZW5lLAozLiBmaXR0aW5nIGEgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsCgojIyMjIDEuIFJ1bm5pbmcgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHBpcGVsaW5lLiAKYGBge3IsIGNhY2hlPVRSVUV9CmRlc2lnbihkZHMyKSA9IH4gY2VsbCArIHRyZWF0bWVudCArIGNlbGw6dHJlYXRtZW50CmRkcyA8LSBERVNlcShkZHMyLCB0ZXN0ID0gIkxSVCIsIHJlZHVjZWQgPSB+IGNlbGwgKyB0cmVhdG1lbnQpCiMgc2F2ZShkZHMsIGZpbGUgPSAiREVTZXFfU0MxLDcsMTBfVGltZWNvdXJzZV9MUlQuUkRhdGEiKQojIGxvYWQoIkRFU2VxX1NDMSw3LDEwX1RpbWVjb3Vyc2VfTFJULlJEYXRhIikKIyBkZHMKYGBgCgojIyMjIDIuIEJ1aWxkaW5nIHRoZSByZXN1bHRzIHRhYmxlLiAKQnkgZGVmYXVsdCwgcmVzdWx0cyB3aWxsIGV4dHJhY3QgdGhlIGVzdGltYXRlZCBsb2cyIGZvbGQgY2hhbmdlcyBhbmQgcCB2YWx1ZXMgZm9yIHRoZSBsYXN0IHZhcmlhYmxlIGluIHRoZSBkZXNpZ24gZm9ybXVsYS4gSWYgdGhlcmUgYXJlIG1vcmUgdGhhbiAyIGxldmVscyBmb3IgdGhpcyB2YXJpYWJsZSwgcmVzdWx0cyB3aWxsIGV4dHJhY3QgdGhlIHJlc3VsdHMgdGFibGUgZm9yIGEgY29tcGFyaXNvbiBvZiB0aGUgbGFzdCBsZXZlbCBvdmVyIHRoZSBmaXJzdCBsZXZlbC4gCmBgYHtyfQojIEVzaW1hdGUgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZ3JvdXBzIGJ5OiAjIGEpIExvd2VyaW5nIHRoZSBGRFIgKHBhZGopIG9yIChiKSByYWlzZSB0aGUgbG9nMiBmb2xkIGNoYW5nZS4KCnJlc3VsdHNOYW1lcyhkZHMpCiMgYWxwaGEgPSBGRFIgYWRqdXN0ZWQgcCB2YWx1ZSBjdXRvZmYKcmVzIDwtIHJlc3VsdHMoZGRzLCBhbHBoYSA9IDAuMDAxKQpzdW1tYXJ5KHJlcykKcmVzT3JkZXJlZCA8LSByZXNbb3JkZXIocmVzJHB2YWx1ZSksXQpyZGF0YSA9IGFzLmRhdGEuZnJhbWUocmVzKQpgYGAKIyMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uOiBkYXlzIDAgdG8gOApDaGFuZ2Ugc2lnbmlmaWNhbnQgbG9nMiBmb2xkIGNoYW5nZSB0byAxLjU4NSAoPT0gMy1mb2xkIGNoYW5nZSBpbiBsb2cyIHNwYWNlKS4KYGBge3J9CnJlc18wdG84ZCA8LSByZXN1bHRzKGRkcywgbmFtZT0idHJlYXRtZW50XzhfdnNfMCIsIGNvb2tzQ3V0b2ZmID0gMC45OSwgCiAgICAgICAgICAgICAgICAgICAgIGluZGVwZW5kZW50RmlsdGVyaW5nID0gVFJVRSwgYWxwaGEgPSAwLjA1LCBwQWRqdXN0TWV0aG9kID0gIkJIIikKc3VtbWFyeShyZXNfMHRvOGQpCiMgb3JkZXIgcmVzdWx0cyB0YWJsZSBieSB0aGUgc21hbGxlc3QgYWRqdXN0ZWQgcCB2YWx1ZToKcmVzXzB0bzhkIDwtIHJlc18wdG84ZFtvcmRlcihyZXNfMHRvOGQkcGFkaiksXQpyZXN1bHRzXzB0bzhkIDwtIGFzLmRhdGEuZnJhbWUocmVzXzB0bzhkKQoKcmVzdWx0c18wdG84ZCA8LSBtdXRhdGUocmVzdWx0c18wdG84ZCwgc2lnPWlmZWxzZShyZXN1bHRzXzB0bzhkJHBhZGo8MC4wNSAmIHJlc3VsdHNfMHRvOGQkbG9nMkZvbGRDaGFuZ2UgPiAxLjU4NSwgIlVwcmVndWxhdGVkIiwgaWZlbHNlKHJlc3VsdHNfMHRvOGQkcGFkajwwLjA1ICYgcmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSA8IC0xLjU4NSwgIkRvd25yZWd1bGF0ZWQiLCAiTm90IFNpZ25pZmljYW50IikpKQoKcm93Lm5hbWVzKHJlc3VsdHNfMHRvOGQpIDwtIHJvdy5uYW1lcyhyZXNfMHRvOGQpCgoKaGVhZChyZXN1bHRzXzB0bzhkKQpERWdlbmVzXzB0bzhkIDwtIHJlc3VsdHNfMHRvOGRbd2hpY2goYWJzKHJlc3VsdHNfMHRvOGQkbG9nMkZvbGRDaGFuZ2UpID4gbG9nMigxLjUpICYgcmVzdWx0c18wdG84ZCRwYWRqIDwgMC4wNSksXQoKaWYoU0FWRUZJTEVTKSB3cml0ZS5jc3YoREVnZW5lc18wdG84ZCwgZmlsZT0ifi9EZXNrdG9wL0RFZ2VuZXNfMHRvOGQuY3N2IikKYGBgCgojIyMgVm9sY2FubyBwbG90CmBgYHtyfQp2b2xjYW5vIDwtIGdncGxvdChyZXN1bHRzXzB0bzhkLCBhZXMobG9nMkZvbGRDaGFuZ2UsIC1sb2cxMChwdmFsdWUpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbCA9IHNpZykpICsgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuMyIpKSArCiAgIyBnZ3RpdGxlKCJWb2xjYW5vIFBsb3Qgb2YgVW50cmVhdGVkIHZzIElkbGluZyIpICsKICBsYWJzKHg9ImxvZzIoRm9sZCBDaGFuZ2UpIiwgeT0iTG9nKE9kZHMgUmF0aW8pIikgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgp2b2xjYW5vCiMgdm9sY2FubyArIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPXJlc3VsdHNfMHRvOGRbMToxMCwgXSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMobGFiZWw9cm93bmFtZXMocmVzdWx0c18wdG84ZFsxOjEwLCBdKSkpCiMgc2F2ZShyZXN1bHRzXzB0bzhkLCBmaWxlPSJ1bnRyZWF0ZWRJZGxpbmdfREVBLlJEYXRhIikKYGBgCgoKYGBge3J9CkRFZ2VuZXNfMHRvOGQgPC0gREVnZW5lc18wdG84ZFtvcmRlcihhYnMoREVnZW5lc18wdG84ZCRsb2cyRm9sZENoYW5nZSksREVnZW5lc18wdG84ZCRzaWcsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCnRlbXAgPC0gREVnZW5lc18wdG84ZFtERWdlbmVzXzB0bzhkJGJhc2VNZWFuID4gMzAwLF0KdGVtcCA8LSB0ZW1wW2Ficyh0ZW1wJGxvZzJGb2xkQ2hhbmdlKT4yLF0KaWYoU0FWRUZJTEVTKSB3cml0ZS5jc3YoREVnZW5lc18wdG84ZCwgZmlsZSA9ICJERWdlbmVzXzB0bzhkLmNzdiIpCmBgYAoKCiMgR2VuZXJhdGluZyBJb24gQ2hhbm5lbCBTcGVjaWZpYyBHZW5lIERhdGFmcmFtZXMKYGBge3J9CnRlc3QgPC0gYXNzYXkoZGRzKQp0eXBlcyA8LSBjKCJBVFAiLCAiVFJQIiwgIkdBQlIiLCAiQ1JBQ1IiLCAiU0xDIiwgIktDTiIsICJDQUNOIiwgIkdSSSIsICJBQkMiLCAiU0NOIiwgIlRSUCIsICJSSUMzIiwgIkNIUk5EIiwgIlJZUiIpCnNhbXBsZXMgPC0gYygiU0MwMV9kYXkwIiwgIlNDMDFfZGF5MyIsICJTQzAxX2RheTgiLCAiU0MwN19kYXkwIiwgIlNDMDdfZGF5MyIsICJTQzA3X2RheTgiLCAiU0MxMF9kYXkwIiwgIlNDMTBfZGF5MyIsICJTQzEwX2RheTgiKQp0ZXN0IDwtIHRlc3RbZ3JlcChwYXN0ZSh0eXBlcywgY29sbGFwc2U9InwiKSwgcm93bmFtZXModGVzdCkpLF0KdGVzdDEgPC0gc2FwcGx5KHNhbXBsZXMsIGZ1bmN0aW9uKHgpIHJvd01lYW5zKHRlc3RbLCBncmVwKHgsIGNvbG5hbWVzKHRlc3QpKV0pKQpyb3duYW1lcyh0ZXN0MSkgPC0gcm93bmFtZXModGVzdCkKdGVzdDEgPC0gdGVzdDFbb3JkZXIocm93bmFtZXModGVzdDEpKSxdCnRlc3QyIDwtIGFzLmRhdGEuZnJhbWUodGVzdDEpCnRlc3QyWyJsMkZDX1NDMDFfMHRvOCJdIDwtIGxvZzIodGVzdDJbIlNDMDFfZGF5OCJdL3Rlc3QyWyJTQzAxX2RheTAiXSkKdGVzdDJbImwyRkNfU0MwN18wdG84Il0gPC0gbG9nMih0ZXN0MlsiU0MwN19kYXk4Il0vdGVzdDJbIlNDMDdfZGF5MCJdKQp0ZXN0MlsibDJGQ19TQzEwXzB0bzgiXSA8LSBsb2cyKHRlc3QyWyJTQzEwX2RheTgiXS90ZXN0MlsiU0MxMF9kYXkwIl0pCnRlc3QzIDwtIHN1YnNldCh0ZXN0MiwgbDJGQ19TQzAxXzB0bzggPiAxICYgbDJGQ19TQzA3XzB0bzggPiAxICYgbDJGQ19TQzEwXzB0bzggPiAxKQp0ZXN0NCA8LSBzdWJzZXQodGVzdDIsIGwyRkNfU0MwMV8wdG84ID4gMSB8IGwyRkNfU0MwN18wdG84ID4gMSB8IGwyRkNfU0MxMF8wdG84ID4gMSkKIyB3cml0ZS5jc3YoeCA9IHRlc3QyLCBmaWxlID0gImFsbF9pb25DaGFubmVsX0V4cHJlc3Npb24uY3N2IikKIyB3cml0ZS5jc3YoeCA9IHRlc3QzLCBmaWxlID0gImFsbFVwcmVnX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQojIHdyaXRlLmNzdih4ID0gdGVzdDQsIGZpbGUgPSAiYXRMZWFzdE9uZVVwcmVnX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQp0ZXN0NSA8LSBsb2cyKHRlc3Q0WywgMTo5XSsxKQpwaGVhdG1hcCh0ZXN0NSwgY2x1c3Rlcl9jb2xzID0gRiwgY2x1c3Rlcl9yb3dzID0gRikKdGVzdDYgPC0gbG9nMigodGVzdDNbLDE6OV0pKzEpCnBoZWF0bWFwKHRlc3Q2LCBjbHVzdGVyX3Jvd3MgPSBGLCBjbHVzdGVyX2NvbHMgPSBGKQp0ZXN0NyA8LSB0ZXN0NVtyb3dTdW1zKHRlc3Q1KT4zMCxdCnBoZWF0bWFwKHRlc3Q3LCBjbHVzdGVyX3Jvd3MgPSBGLCBjbHVzdGVyX2NvbHMgPSBGKQpgYGAKCmBgYHtyfQojIGxvYWQoZmlsZT0idW50cmVhdGVkSWRsaW5nX0RFQS5SRGF0YSIpCgpPcmdEQiA8LSBvcmcuSHMuZWcuZGIKdXByZWdfZ2VuZXMgPC0gc3Vic2V0KHJlc3VsdHNfMHRvOGQsIHBhZGo8MC4wNSAmIGxvZzJGb2xkQ2hhbmdlPjIpCmRvd25yZWdfZ2VuZXMgPC1zdWJzZXQocmVzdWx0c18wdG84ZCwgcGFkajwwLjA1ICYgbG9nMkZvbGRDaGFuZ2U8KC0yKSkKCmdlbmVMaXN0X3VwIDwtIGFzLnZlY3Rvcih1cHJlZ19nZW5lcyRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfdXApIDwtIHJvd25hbWVzKHVwcmVnX2dlbmVzKQpnZW5lTGlzdF9kb3duIDwtIGFzLnZlY3Rvcihkb3ducmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKQpuYW1lcyhnZW5lTGlzdF9kb3duKSA8LSByb3duYW1lcyhkb3ducmVnX2dlbmVzKQoKZ2VuZXNfdXAgPC0gYXMudmVjdG9yKHJvd25hbWVzKHVwcmVnX2dlbmVzKSkKZ2VuZXNfZG93biA8LSBhcy52ZWN0b3Iocm93bmFtZXMoZG93bnJlZ19nZW5lcykpCiMgbmFtZXMoZ2VuZUxpc3QpIDwtIHJvd25hbWVzKHJlc3VsdHNfMHRvOGQpCmdlbmVzX3VwX0VOVFJFWklEIDwtIGJpdHIoZ2VuZXNfdXAsIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9ICJFTlRSRVpJRCIsIE9yZ0RiID0gT3JnREIpJEVOVFJFWklECmdlbmVzX2Rvd25fRU5UUkVaSUQgPC0gYml0cihnZW5lc19kb3duLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRAoKIyBHcm91cCBHTwpnZ29fdXAgPC0gY2x1c3RlclByb2ZpbGVyOjpncm91cEdPKGdlbmUgICAgID0gZ2VuZXNfdXBfRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgICAgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlID0gVFJVRSkKZ2dvX3VwX2RmIDwtIGFzLmRhdGEuZnJhbWUoZ2dvX3VwKQpnZ29fdXBfZGYgPC0gZ2dvX3VwX2RmW29yZGVyKC1nZ29fdXBfZGYkQ291bnQpLF0gCgpnZ29fZG93biA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSA9IGdlbmVzX2Rvd25fRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgICAgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlID0gVFJVRSkKIyBWaWV3KGFzLmRhdGEuZnJhbWUoZ2dvX2Rvd24pKQoKIyBHTyBvdmVyLXJlcHJlc2VudGF0aW9uIHRlc3QKZWdvX2dlbmVzVXAgPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lICA9IGdlbmVzX3VwX0VOVFJFWklELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgID0gT3JnREIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmICA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgICAgICA9IFRSVUUpCgojIFZpZXcoYXMuZGF0YS5mcmFtZShlZ29fZ2VuZXNVcCkpCgplZ29fZ2VuZXNEb3duIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSAgPSBnZW5lc19kb3duX0VOVFJFWklELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgID0gT3JnREIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmICA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgICAgICA9IFRSVUUpCgojIFZpZXcoYXMuZGF0YS5mcmFtZShlZ29fZ2VuZXNEb3duKSkKCiMga2tfZ2VuZXNVcCA8LSBlbnJpY2hLRUdHKGdlbmUgPSBnZW5lc191cF9FTlRSRVpJRCwKIyAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAnaHNhJywKIyAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQojIFZpZXcoYXMuZGF0YS5mcmFtZShra19nZW5lc1VwKSkKIyAKIyBra19nZW5lc0Rvd24gPC0gZW5yaWNoS0VHRyhnZW5lID0gZ2VuZXNfZG93bl9FTlRSRVpJRCwKIyAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAnaHNhJywKIyAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQojIFZpZXcoYXMuZGF0YS5mcmFtZShra19nZW5lc0Rvd24pKQoKIyBlZ29fR1NFQV91cCA8LSBnc2VHTyhnZW5lTGlzdCA9IGdlbmVMaXN0X3VwLAojICAgICAgICAgICAgICAgT3JnRGIgICAgICAgID0gT3JnREIsCiMgICAgICAgICAgICAgICBvbnQgICAgICAgICAgPSAiQlAiLAojICAgICAgICAgICAgICAgblBlcm0gICAgICAgID0gMTAwMCwKIyAgICAgICAgICAgICAgIG1pbkdTU2l6ZSAgICA9IDEwMCwKIyAgICAgICAgICAgICAgIG1heEdTU2l6ZSAgICA9IDUwMCwKIyAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUsCiMgICAgICAgICAgICAgICB2ZXJib3NlICAgICAgPSBGQUxTRSkKCiMgYmFycGxvdChnZ29fdXAsIG9yZGVyPVQpCiMgYmFycGxvdChnZ29fZG93bikKZG90cGxvdChlZ29fZ2VuZXNVcCkgKyBnZ3RpdGxlKCJHTyBPdmVyLXJlcHJlc2VudGF0aW9uIFVwcmVndWxhdGVkIEdlbmVzIikgKwogIGxhYnMoeD0iR2VuZSBSYXRpbyIsIHk9IkdPIFRlcm1zIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSkKCmRvdHBsb3QoZWdvX2dlbmVzRG93bikgKyBnZ3RpdGxlKCJHTyBPdmVyLXJlcHJlc2VudGF0aW9uIERvd25yZWd1bGF0ZWQgR2VuZXMiKSArCiAgbGFicyh4PSJHZW5lIFJhdGlvIiwgeT0iR08gVGVybXMiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQoKIyBlbWFwcGxvdChlZ29fZ2VuZXNVcCkKIyBlbWFwcGxvdChlZ29fZ2VuZXNEb3duKQpjbmV0cGxvdChlZ29fZ2VuZXNVcCwgY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBmb2xkQ2hhbmdlPWdlbmVMaXN0X3VwKQpjbmV0cGxvdChlZ29fZ2VuZXNEb3duLCBjYXRlZ29yeVNpemU9InB2YWx1ZSIsIGZvbGRDaGFuZ2U9Z2VuZUxpc3RfZG93bikKCgplZ29fZ2VuZXNVcF9kZiA8LSBhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSAKZWdvVXAgPC0gZWdvX2dlbmVzVXBfZGZbb3JkZXIoLWVnb19nZW5lc1VwX2RmJENvdW50KSxdCiMgc29ydGVkX2Vnb1VwX3RvcDEwIDwtIGhlYWQoZWdvVXAsIDEwKQplZ29VcF9nZW5lcyA8LSBzdHJzcGxpdChlZ29VcCRnZW5lSUQsICIvIiwgZml4ZWQ9VFJVRSkKIyBlZ29VcF90b3AxMF9nZW5lc19hbGwgPC0gdW5saXN0KHN0cnNwbGl0KGhlYWQoZWdvVXAsIDEwKSRnZW5lSUQsICJbL10iKSkKIyBlZ29VcF90b3AxMF9nZW5lc19ncm91cCA8LSBzdHJzcGxpdChzb3J0ZWRfZWdvVXBfdG9wMTAkZ2VuZUlELCAiWy9dIikKIyBlZ29VcF90b3AxMF9nZW5lc191bmlxdWUgPC0gdW5pcXVlKGVnb1VwX3RvcDEwX2dlbmVzKQojIHRhYmxlKGVnb1VwX3RvcDEwX2dlbmVzKQojIGVnb1VwX2dlbmVzQnlHcm91cCA8LSBhcy5kYXRhLmZyYW1lKHQocGx5cjo6bGRwbHkoZWdvVXBfdG9wMTBfZ2VuZXNfZ3JvdXAsIHJiaW5kKSkpCiMgY29sbmFtZXMoZWdvVXBfZ2VuZXNCeUdyb3VwKSA8LSBzb3J0ZWRfZWdvVXBfdG9wMTAkRGVzY3JpcHRpb24KIyBlZ29VcF9nZW5lc0J5R3JvdXBfaW9uT25seSA8LSBlZ29VcF9nZW5lc0J5R3JvdXBbLGMoMTo2LDg6MTApXQoKIyB3cml0ZS5jc3YoZWdvVXBfZ2VuZXNCeUdyb3VwLCBmaWxlPSJ0b3AxMEdPdGVybXNVcHJlZ3VsYXRlZF9nZW5lTWVtYmVyc2hpcC5jc3YiKQojIGlvbkdlbmVzIDwtIHVuaXF1ZSh1bmxpc3QoZWdvVXBfZ2VuZXNCeUdyb3VwX2lvbk9ubHkpKQojIAojIGVuc2VtYmwgPSB1c2VFbnNlbWJsKGJpb21hcnQ9ImVuc2VtYmwiLCBkYXRhc2V0PSJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiKQojIElEcyA8LSBhcy5jaGFyYWN0ZXIoaW9uR2VuZXMpCiMgZ2VuZVVwSUQgPC0gbmFtZXMoZ2VuZUxpc3RfdXApCiMgZ2VuZURvd25JRCA8LSBuYW1lcyhnZW5lTGlzdF9kb3duKQojIGdlbmVkZXNjX2lvbiA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2V4dGVybmFsX2dlbmVfbmFtZScsJ2Rlc2NyaXB0aW9uJyksIGZpbHRlcnMgPSAnZXh0ZXJuYWxfZ2VuZV9uYW1lJywgdmFsdWVzID0gSURzLCBtYXJ0ID1lbnNlbWJsKQojIHdyaXRlLmNzdihnZW5lZGVzY19pb24sIGZpbGUgPSAiaW9uQ2hhbm5lbEdlbmVzX2Rlc2NyaXB0aW9uLmNzdiIpCgojIGdlbmVkZXNjX1VwIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygnZXh0ZXJuYWxfZ2VuZV9uYW1lJywnZGVzY3JpcHRpb24nKSwgZmlsdGVycyA9ICdleHRlcm5hbF9nZW5lX25hbWUnLCB2YWx1ZXMgPSBnZW5lVXBJRCwgbWFydCA9ZW5zZW1ibCkKIyB3cml0ZS5jc3YoZ2VuZWRlc2NfVXAsIGZpbGUgPSAidXByZWd1bGF0ZWRHZW5lc19kZXNjcmlwdGlvbi5jc3YiKQojIGdlbmVkZXNjX0Rvd24gPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdleHRlcm5hbF9nZW5lX25hbWUnLCdkZXNjcmlwdGlvbicpLCBmaWx0ZXJzID0gJ2V4dGVybmFsX2dlbmVfbmFtZScsIHZhbHVlcyA9IGdlbmVEb3duSUQsIG1hcnQgPWVuc2VtYmwpCiMgd3JpdGUuY3N2KGdlbmVkZXNjX0Rvd24sIGZpbGUgPSAiZG93bnJndWxhdGVkR2VuZXNfZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKYGBge3J9CmdlbmVMaXN0X2FsbCA8LSBhcy52ZWN0b3IocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfYWxsKSA8LSByb3duYW1lcyhyZXN1bHRzXzB0bzhkKQphIDwtIG5hbWVzKGdlbmVMaXN0X2FsbCkKZ2VuZXNfRU5UUkVaSUQgPC0gYml0cihhLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRApuYW1lcyhnZW5lTGlzdF9hbGwpIDwtIGdlbmVzX0VOVFJFWklECgpnZW5lX2RmIDwtIGRhdGEuZnJhbWUoRW50cmV6PW5hbWVzKGdlbmVMaXN0X2FsbCksIEhHTkM9YSwgRkM9Z2VuZUxpc3RfYWxsKQpnZW5lX2RmIDwtIGdlbmVfZGZbYWJzKGdlbmVfZGYkRkMpID4gMSxdCmdlbmVfZGYkZ3JvdXAgPC0gInVwcmVndWxhdGVkIgpnZW5lX2RmJGdyb3VwW2dlbmVfZGYkRkMgPCAwXSA8LSAiZG93bnJlZ3VsYXRlZCIKZ2VuZV9kZiRvdGhlcmdyb3VwIDwtICJBIgpnZW5lX2RmJG90aGVyZ3JvdXBbYWJzKGdlbmVfZGYkRkMpID4gMl0gPC0gIkIiCgpmb3JtdWxhX3JlcyA8LSBjb21wYXJlQ2x1c3RlcihFbnRyZXp+Z3JvdXArb3RoZXJncm91cCwgZGF0YT1nZW5lX2RmLCBmdW49ImVucmljaEtFR0ciKQpoZWFkKGFzLmRhdGEuZnJhbWUoZm9ybXVsYV9yZXMpKQoKYGBgCiMjIyMgMy4gRXhwbG9yaW5nIFJlc3VsdHMKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltPWMoLTIsMikpCnBsb3RDb3VudHMoZGRzLCBnZW5lPXdoaWNoLm1pbihyZXMkcGFkaiksIGludGdyb3VwPSJ0cmVhdG1lbnQiKQoKYGBgCgojIyMjIExvZyBub3JtYWxpemUgcmVzdWx0cyAjIyMjCmBgYHtyfQoKIyBub3JtYWxpemVkQ291bnRzIDwtIHQoIHQoY291bnRzKGRkcykpIC8gc2l6ZUZhY3RvcnMoZGRzKSApCgojbG9nMiBub3JtYWxpemVkIGNvdW50cwpybGQyIDwtIHJsb2coZGRzLCBibGluZCA9IEZBTFNFKQojIHNhdmUocmxkMiwgZmlsZSA9ICJSTEQyX1NDMSw3LDEwX1RpbWVjb3Vyc2VfaG1hcC5SRGF0YSIpCgojIGxvYWQoIlJMRDJfU0MxLDcsMTBfVGltZWNvdXJzZV9obWFwLlJEYXRhIikKYGBgCgojIyMjIENsdXN0ZXJpbmcgIyMjCgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHJsZDIpKSkKc2FtcGxlRGlzdHMKCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KCBzYW1wbGVEaXN0cyApCnJvd25hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIHBhc3RlKHJsZDIkdHJlYXRtZW50LCBybGQyJGNlbGwsIHNlcCA9ICIgLSAiICkKY29sbmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gTlVMTApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBzYW1wbGVEaXN0cywKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gc2FtcGxlRGlzdHMsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCgpwb2lzZCA8LSBQb2lDbGFDbHU6OlBvaXNzb25EaXN0YW5jZSh0KGNvdW50cyhkZHMpKSkKc2FtcGxlUG9pc0Rpc3RNYXRyaXggPC0gYXMubWF0cml4KCBwb2lzZCRkZCApCnJvd25hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBwYXN0ZSggZGRzJGRleCwgZGRzJGNlbGwsIHNlcD0iIC0gIiApCmNvbG5hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBOVUxMCnBoZWF0bWFwKHNhbXBsZVBvaXNEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBwb2lzZCRkZCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gcG9pc2QkZGQsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCm1kcyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMikpICAlPiUKICAgICAgICAgY2JpbmQoY21kc2NhbGUoc2FtcGxlRGlzdE1hdHJpeCkpCmdncGxvdChtZHMsIGFlcyh4ID0gYDFgLCB5ID0gYDJgLCBjb2xvciA9IGNlbGwsIHNoYXBlID0gdHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgY29vcmRfZml4ZWQoKSArIHRoZW1lX2J3KCkgKwogIHhsYWIoIlBDMSIpICsgeWxhYigiUEMyIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICAjIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKIyBsaWJyYXJ5KCJnZW5lZmlsdGVyIikKdG9wVmFyR2VuZXMgPC0gaGVhZChvcmRlcihyb3dWYXJzKGFzc2F5KHJsZDIpKSwgZGVjcmVhc2luZyA9IFRSVUUpLCA1MDAwKQptYXQgIDwtIGFzc2F5KHJsZDIpWyB0b3BWYXJHZW5lcywgXQptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMilbLCBjKCJjZWxsIiwidHJlYXRtZW50IildKQpuYW1lcyhhbm5vKSA8LSBjKCJDZWxsIiwgIlRyZWF0bWVudCIpCmFubm90YXRpb25fY29sb3JzID0gbGlzdCgKICBDZWxsID0gYyhTQzAxPSJyZWQyIiwgU0MwNz0iZ3JlZW4yIiwgU0MxMD0iYmx1ZTIiKSwKICBUcmVhdG1lbnQgPSBjKCIwIj0iY3lhbjIiLCAiMyI9ImRhcmtvcmFuZ2UiLCAiOCI9ImRhcmtvcmNoaWQiKSkKcGhlYXRtYXAobWF0LCBhbm5vdGF0aW9uX2NvbCA9IGFubm8sIHNob3dfcm93bmFtZXMgPSBGLCBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9ycykKYGBgCgojIyMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzICMjIyMKCiMgMSBERVNlcTIgdGltZSBzZXJpZXMgYW5hbHlzaXMKYGBge3J9CiMgYnJvd3NlVmlnbmV0dGVzKCJybmFzZXFHZW5lIikKCmRkc1RDIDwtIERFU2VxKGRkcywgdGVzdD0iTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKcmVzVEMgPC0gcmVzdWx0cyhkZHNUQykKcmVzVEMkc3ltYm9sIDwtIG1jb2xzKGRkc1RDKSRzeW1ib2wKIyBoZWFkKHJlc1RDW29yZGVyKHJlc1RDJHBhZGopLF0sIDQpCgp0YyA8LSBwbG90Q291bnRzKGRkc1RDLCB3aGljaC5taW4ocmVzVEMkcGFkaiksIAogICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0cmVhdG1lbnQiLCJjZWxsIiksIHJldHVybkRhdGEgPSBUUlVFKQoKZGRzVENbd2hpY2gubWluKHJlc1RDJHBhZGopLF0KCmdncGxvdCh0YywKICBhZXMoeCA9IHJlcChjKDAsMyw4KSwgZWFjaD05KSwgeSA9IGNvdW50LCBjb2xvciA9IGNlbGwsIGdyb3VwID0gY2VsbCkpICsgCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbWV0aG9kID0gImxvZXNzIikgKyBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lX2J3KCkgKwogIGdndGl0bGUoIlRpbWUgQ291cnNlIEV4cHJlc3Npb24gb2YgUERLNCIpICsKICBsYWJzKHg9IlRpbWUgKGRheXMpIiwgeT0iR2VuZSBDb3VudCIpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKcmVzdWx0c05hbWVzKGRkc1RDKQoKYmV0YXMgPC0gY29lZihkZHNUQykKY29sbmFtZXMoYmV0YXMpCgp0b3BHZW5lcyA8LSBoZWFkKG9yZGVyKHJlc1RDJHBhZGopLDUwKQptYXQgPC0gYmV0YXNbdG9wR2VuZXMsIC1jKDEsMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKcGhlYXRtYXAobWF0LCBicmVha3M9c2VxKGZyb209LXRociwgdG89dGhyLCBsZW5ndGg9MTAxKSwKICAgICAgICAgY2x1c3Rlcl9jb2w9RkFMU0UpCmBgYAoKCiMjIyBOT1RFCk9yaWdpbmFsIGNvZGUgYmVsb3cgcHJvZHVjZWQgbWFueSBtZXNzYWdlcyBvZiBgTm8gaWQgdmFyaWFibGVzOyB1c2luZyBhbGwgYXMgbWVhc3VyZSB2YXJpYWJsZXNgOyBwcmVzdW1hYmx5IGEgbGluZSBmb3IgZWFjaCBnZW5lLiBUaGlzIGlzIGR1ZSB0byB0aGUgYG1lbHRgIGZ1bmN0aW9uIG5vdCBoYXZpbmcgYW55IGlkIHZhcmlhYmxlcyB0byB1c2UuICAKCgpSZWppZ2dlcmluZyBjb2RlIG5vdCB5ZXQgZmluaXNoZWQuICBTaG91bGQgcHJvYmFibHkgdXNlIApgYGB7ciBTdWJsaW5lIEFOT1ZBLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIDEuMSBBTk9WQSAtIGNvbXBhcmUgYnR3biBzdWJsaW5lcyAKIyBncm91cCA8LSBhcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX2Jhc2VsaW5lIDwtIGxpc3QoKQpUdWtleVNDMDd0b1NDMDEgPC0gbGlzdCgpClR1a2V5U0MxMHRvU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzEwdG9TQzA3IDwtIGxpc3QoKQpub3JtX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQyKSlbYygnU0MwMV9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMycpXQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIE5ldyBjb2RlIGJ5IERSVCAjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHNhbXBfbmFtZXMgPC0gY29sbmFtZXMobm9ybV9kYXRhKQoKIyBjb21wYXJlU3ViY2xvbmVzIDwtIGZ1bmN0aW9uKGdlbmVfbmFtZSwgZGF0PW5vcm1fZGF0YSwgc2FtcF9uYW1lcz1OVUxMLCBncm91cD1OVUxMKQojIHsKIyAgICAgaWYoaXMubnVsbChncm91cCkpIGdyb3VwIDwtIGFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyAgICAgaWYoaXMubnVsbChzYW1wX25hbWVzKSkgc2FtcF9uYW1lcyA8LSBjb2xuYW1lcyhkYXQpCiMgICAgICMgZGZhID0gZGF0YSBmb3IgYW5hbHlzaXMKIyAgICAgZGZhIDwtIGRhdGEuZnJhbWUodmFsdWU9YXMubnVtZXJpYyh0KGRhdFtnZW5lX25hbWUsXSkpLCBncm91cD1ncm91cCkKIyAgICAgcm93bmFtZXMoZGZhKSA8LSBzYW1wX25hbWVzCiMgICAgIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGRmYSkKIyAgICAgYW5vdmFfYmFzZWxpbmUgPC0gc3VtbWFyeShmaXQpW1sxXV1bWydQcig+RiknXV1bMV0KIyAgICAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQojICAgICBwdmFsIDwtIGRhdGEuZnJhbWUocF9hZGo9cmVzdWx0cyRncm91cFssJ3AgYWRqJ10pCiMgICAgIHJvd25hbWVzKHB2YWwpIDwtIGMoIlR1a2V5U0MwN3RvU0MwMSIsIlR1a2V5U0MxMHRvU0MwMSIsIlR1a2V5U0MxMHRvU0MwNyIpCiMgICAgIG91dCA8LSBsaXN0KGFub3ZhX2Jhc2VsaW5lID0gYW5vdmFfYmFzZWxpbmUsCiMgICAgICAgICAgICAgICAgIHB2YWwgPSBwdmFsKQojICAgICAjIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQojICAgICAjIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQojICAgICAjIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXSAgICAKIyAgICAgcmV0dXJuKG91dCkKIyB9CgojIHRlbXAgPC0gbGFwcGx5KHJvd25hbWVzKG5vcm1fZGF0YSksIGNvbXBhcmVTdWJjbG9uZXMpCiMgYW5vdmFfcHZhbCA8LSBzYXBwbHkodGVtcCwgIltbIiwgMSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMgRW5kIG5ldyBjb2RlICMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmb3IgKGdlbmUgaW4gMTpucm93KG5vcm1fZGF0YSkpIHsKICBnZW5lX25vcm1fZGF0YSA8LSBub3JtX2RhdGFbZ2VuZSxdCiAgIyBkMyA8LSBkYXRhLmZyYW1lKHkgPSBnZW5lX25vcm1fZGF0YSwgZ3JvdXAgPSBncm91cCkKICAjIGZpdCA8LSBsbSh5fmdyb3VwLCBkMykKICBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIGRhdGEuZnJhbWUodmFyaWFibGU9Y29sbmFtZXMoZ2VuZV9ub3JtX2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT1hcy5udW1lcmljKHQoZ2VuZV9ub3JtX2RhdGEpKSkKICBnZW5lX25vcm1fZGF0YV9tZWx0JGdyb3VwIDwtIGdyb3VwCiAgZml0IDwtIGFvdih2YWx1ZX5ncm91cCwgZ2VuZV9ub3JtX2RhdGFfbWVsdCkKICAjIGFub3ZhX2xpc3RbZ2VuZV0gPC0gYW5vdmEoZml0KSQnUHIoPkYpJ1sxXQogIGFub3ZhX2Jhc2VsaW5lW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKCiAgCmFub3ZhX3B2YWwgPC0gdW5saXN0KGFub3ZhX2Jhc2VsaW5lKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzA3dG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MwN3RvU0MwMSkKVHVrZXlTQzEwdG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwMSkKVHVrZXlTQzEwdG9TQzA3X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwNykKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBhbm92YV9wdmFsKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBUdWtleVNDMDd0b1NDMDFfcHZhbCkKbm9ybV9kYXRhX3N0YXRzIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0cywgVHVrZXlTQzEwdG9TQzAxX3B2YWwpCm5vcm1fZGF0YV9zdGF0cyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHMsIFR1a2V5U0MxMHRvU0MwN19wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0cywgZmlsZSA9ICJzdWJjbG9uZUNvdW50c19hbm92YV90dWtleV9ERVNlcTIuUkRhdGEiKQoKIyBJZGVudGlmeSBnZW5lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIGNsb25lcyBiYXNlZCBvbiAKIyBBTk9WQSBwLXZhbHVlIHdpdGggZGVmaW5lZCB0aHJlc2hvbGQKc2lnVGhyZXNoIDwtIDAuMDUKdGFibGUoYW5vdmFfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3dG9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMHRvU0MwMV9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTB0b1NDMDdfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c1siYW5vdmFfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGwgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzWyJhbm92YV9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNbIlR1a2V5U0MwN3RvU0MwMV9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c1siVHVrZXlTQzEwdG9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzWyJUdWtleVNDMTB0b1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c1tzaWdJbmRlY2llcyxdKQpzaWdEaWZmR2VuZXNBbGwgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzW3NpZ0luZGVjaWVzQWxsLF0pCmBgYAoKIyAyLiBBTk9WQSBidHduIHRpbWUgcG9pbnRzICYgc2hhcmVkIGJ0d24gc3VibGluZXMpCmBgYHtyIFNDMDEgdGltZSBBTk9WQSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzAxX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMDFfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MwMV90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMDF0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMDFfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MwMXRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMDF0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgIyBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIG1lbHQoZ2VuZV9ub3JtX2RhdGEpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCgogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwMVtnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDFfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDFfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDFfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDFfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwMSkgIyBtYWtlIGFycmF5ClR1a2V5U0MwMV90aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTApClR1a2V5U0MwMV90aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTMpClR1a2V5U0MwMV90aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDF0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgYW5vdmFfU0MwMV9wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgVHVrZXlTQzAxX3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBUdWtleVNDMDFfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDEgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDEsIFR1a2V5U0MwMV90aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzAxdGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwMV90aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDFfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzAxX3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzAxIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzAxWyJhbm92YV9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDEgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDFbImFub3ZhX1NDMDFfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDFbIlR1a2V5U0MwMV90aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzAxWyJUdWtleVNDMDFfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwMVsiVHVrZXlTQzAxX3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzAxIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzAxW3NpZ0luZGVjaWVzX1NDMDEsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDEgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDFbc2lnSW5kZWNpZXNBbGxfU0MwMSxdKQpgYGAKCgpgYGB7ciBTQzA3IHRpbWUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9Cmdyb3VwPC1hcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX1NDMDcgPC0gbGlzdCgpClR1a2V5U0MwN190aW1lMCA8LSBsaXN0KCkKVHVrZXlTQzA3X3RpbWUzIDwtIGxpc3QoKQpUdWtleVNDMDdfdGltZTggPC0gbGlzdCgpCm5vcm1fZGF0YV9TQzA3dGltZSA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KHJsZDIpKVtjKCdTQzA3X2RheTBfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTNfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkzX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5M19yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheThfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXk4X3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5OF9yZXAzJyldCmZvciAoZ2VuZSBpbiAxOm5yb3cobm9ybV9kYXRhX1NDMDd0aW1lKSkgewogIGdlbmVfbm9ybV9kYXRhIDwtIG5vcm1fZGF0YV9TQzA3dGltZVtnZW5lLF0KICAjIGQzIDwtIGRhdGEuZnJhbWUoeSA9IGdlbmVfbm9ybV9kYXRhLCBncm91cCA9IGdyb3VwKQogICMgZml0IDwtIGxtKHl+Z3JvdXAsIGQzKQogICMgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBtZWx0KGdlbmVfbm9ybV9kYXRhKQogIGdlbmVfbm9ybV9kYXRhX21lbHQgPC0gZGF0YS5mcmFtZSh2YXJpYWJsZT1jb2xuYW1lcyhnZW5lX25vcm1fZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPWFzLm51bWVyaWModChnZW5lX25vcm1fZGF0YSkpKQogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwN1tnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDdfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDdfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDdfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDdfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwNykgIyBtYWtlIGFycmF5ClR1a2V5U0MwN190aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTApClR1a2V5U0MwN190aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTMpClR1a2V5U0MwN190aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDd0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgYW5vdmFfU0MwN19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgVHVrZXlTQzA3X3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBUdWtleVNDMDdfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDcgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDcsIFR1a2V5U0MwN190aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzA3dGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzA3X3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwN190aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDdfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3X3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzA3IDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzA3WyJhbm92YV9TQzA3X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDcgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDdbImFub3ZhX1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDdbIlR1a2V5U0MwN190aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzA3WyJUdWtleVNDMDdfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwN1siVHVrZXlTQzA3X3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzA3IDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzA3W3NpZ0luZGVjaWVzX1NDMDcsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDcgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDdbc2lnSW5kZWNpZXNBbGxfU0MwNyxdKQpgYGAKCmBgYHtyIFNDMTAgdGltZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MxMCA8LSBsaXN0KCkKVHVrZXlTQzEwX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMTBfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MxMF90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMTB0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMTBfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MxMHRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMTB0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCRncm91cCA8LSBncm91cAogIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGdlbmVfbm9ybV9kYXRhX21lbHQpCiAgIyBhbm92YV9saXN0W2dlbmVdIDwtIGFub3ZhKGZpdCkkJ1ByKD5GKSdbMV0KICBhbm92YV9TQzEwW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MxMF90aW1lMFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMF90aW1lM1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMF90aW1lOFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQogIAp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKYW5vdmFfU0MxMF9wdmFsIDwtIHVubGlzdChhbm92YV9TQzEwKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzEwX3RpbWUwX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMCkKVHVrZXlTQzEwX3RpbWUzX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMykKVHVrZXlTQzEwX3RpbWU4X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lOCkKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gYXMuZGF0YS5mcmFtZShub3JtX2RhdGFfU0MxMHRpbWUpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBhbm92YV9TQzEwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBUdWtleVNDMTBfdGltZTBfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMTAsIFR1a2V5U0MxMF90aW1lM19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MxMCA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MxMCwgVHVrZXlTQzEwX3RpbWU4X3B2YWwpCgojIHNhdmUobm9ybV9kYXRhX3N0YXRzX1NDMTAsIGZpbGUgPSAic3ViY2xvbmVDb3VudHNfYW5vdmFfdHVrZXlfREVTZXEyX1NDMTB0aW1lLlJEYXRhIikKCiMgSWRlbnRpZnkgZ2VuZXMgdGhhdCBkaWZmZXIgYmV0d2VlbiBjbG9uZXMgYmFzZWQgb24gCiMgQU5PVkEgcC12YWx1ZSB3aXRoIGRlZmluZWQgdGhyZXNob2xkCnNpZ1RocmVzaCA8LSAwLjA1CnRhYmxlKGFub3ZhX1NDMTBfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzEwX3RpbWUwX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMF90aW1lM19wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTBfdGltZThfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzX1NDMTAgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMTBbImFub3ZhX1NDMTBfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGxfU0MxMCA8LSB3aGljaChub3JtX2RhdGFfc3RhdHNfU0MxMFsiYW5vdmFfU0MxMF9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MxMFsiVHVrZXlTQzEwX3RpbWUwX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMTBbIlR1a2V5U0MxMF90aW1lM19wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzEwWyJUdWtleVNDMTBfdGltZThfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzX1NDMTAgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMTBbc2lnSW5kZWNpZXNfU0MxMCxdKQpzaWdEaWZmR2VuZXNBbGxfU0MxMCA8LSByb3duYW1lcyhub3JtX2RhdGFfc3RhdHNfU0MxMFtzaWdJbmRlY2llc0FsbF9TQzEwLF0pCmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmFsbF9TQ3NfdGltZSA8LSBSZWR1Y2UoaW50ZXJzZWN0LCAKICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHNpZ0RpZmZHZW5lc0FsbF9TQzAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnRGlmZkdlbmVzQWxsX1NDMDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdEaWZmR2VuZXNBbGxfU0MxMCkpCgpkZl9hbGxTQ3NfdGltZSA8LSBkYXRhLmZyYW1lKGdlbmUgPSBhbGxfU0NzX3RpbWUpCiMgZ2VuZXMgPC0gZGZfYWxsU0NzX3RpbWUkZ2VuZQojIEdfbGlzdCA8LSBnZXRCTShmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwgYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSx2YWx1ZXM9Z2VuZXMsbWFydD1tYXJ0KQptZXJnZShkZl9hbGxTQ3NfdGltZSxHX2xpc3QsYnkueD0iZ2VuZSIsYnkueT0iZW5zZW1ibF9nZW5lX2lkIikKCiMgd3JpdGUuY3N2KEdfbGlzdCwgZmlsZSA9ICJBTk9WQV9hbGxTQ3NUaW1lX3NoYXJlZF9nZW5lcy5jc3YiKQoKIyBDb21wYXJlIGdlbmUgbGlzdHMgZm9yIGJldHdlZW4gc3VibGluZXMgYW5kIHRpbWUKIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKZGJzIDwtIGxpc3RFbnJpY2hyRGJzKCkKZGJzIDwtIGMoIktFR0dfMjAxNiIsIAogICAgICAgICAiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTgiKQoKZW5yaWNoZWRfYWxsU0NzdGltZSA8LSBlbnJpY2hyKEdfbGlzdCRoZ25jX3N5bWJvbCwgZGJzKQoKS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUgPC0gZW5yaWNoZWRfYWxsU0NzdGltZVtbIktFR0dfMjAxNiJdXVsxOjUsXQpLRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtcyA8LSBmYWN0b3IoS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUkVGVybSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1LRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtKQoKZ2dwbG90KEtFR0dfdXByZWdfYWxsU0NzdGltZV90b3A1LCAKICAgICAgIGFlcyh4PVRlcm1zLCB5PS1sb2cxMChBZGp1c3RlZC5QLnZhbHVlKSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICJQYXRod2F5IFRlcm0iLCB5ID0gIi1sb2cxMChxLXZhbHVlKSIpICArCiAgdGhlbWVfYncoKSAgKyBnZ3RpdGxlKCJQYXRod2F5IEVucmljaG1lbnQgLSBLRUdHIDIwMTYiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCiAgCgpgYGAKCiMgMyBKYWNrJ3MgbWV0aG9kIApgYGB7ciBldmFsPUZBTFNFfQojR3JhYiBhbGwgdGhlIG5hbWVzIGZyb20gcmVzIGluIHRoZSBERVNlcSBtYXRyaXgKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCgpjb3VudE1BVCA8LSBkYXRhLmZyYW1lKG5vcm1hbGl6ZWRDb3VudHNbdG9wR2VuZXMsXSkKCnN1YnJsID0gZGF0YS5mcmFtZShhc3NheShybGQyKSkKcmxNQVQgPSBkYXRhLmZyYW1lKHN1YnJsW3RvcEdlbmVzLF0pCgojTGFiZWxpbmcgcm93cyB3aXRoIEVOU0cgSURzCiMgY291bnRNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKGNvdW50TUFUKQojIGNvdW50TUFUJHBhZGogPSByZXNbdG9wR2VuZXMsInBhZGoiXQoKcmxNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKHJsTUFUKQpybE1BVCRwYWRqID0gcmVzW3RvcEdlbmVzLCJwYWRqIl0KCiMgbGlicmFyeShiaW9tYVJ0KQojIGVuc2VtYmwgPC0gdXNlTWFydCgiZW5zZW1ibCIpCiMgbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKIyBnZW5lcyA9IHJvdy5uYW1lcyhybE1BVCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKI0NoZWNrIGlmIGRhdGEgZml0cyBhIG5vcm1hbCBkaXN0cmlidXRpb24KIyBwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgoY291bnRNQVRbLDE6MjddKSkpKQpwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgocmxNQVRbLDE6MjddKSkpKQoKCiNybE1BVCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlcmVmb3JlIHdlIHdpbGwgdXNlIHRoaXMgaW4gdGhlIGhlYXRtYXAgY29uc3RydWN0aW9uCiNMYWJlbGluZyBkZiB3aXRoIGhnbmMgc3ltYm9scwpHRV9kYXRhIDwtIG1lcmdlKEdfbGlzdCwgcmxNQVQsIGJ5ID0gImVuc2VtYmxfZ2VuZV9pZCIpCgojTWFraW5nIHJvd25hbWVzIHVuaXF1ZSBoZ25jIHN5bWJvbHMKcm93bmFtZXMoR0VfZGF0YSkgPC0gbWFrZS5uYW1lcyhHRV9kYXRhWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI0F2ZXJhZ2luZyBybGQgYmV0d2VlbiB0cmlhbHMKQWNvbCA8LSBjKCJTQzAxX2RheTAiLAogICAgICAgICAgIlNDMDFfZGF5MyIsCiAgICAgICAgICAiU0MwMV9kYXk4IiwKICAgICAgICAgICJTQzA3X2RheTAiLAogICAgICAgICAgIlNDMDdfZGF5MyIsCiAgICAgICAgICAiU0MwN19kYXk4IiwKICAgICAgICAgICJTQzEwX2RheTAiLAogICAgICAgICAgIlNDMTBfZGF5MyIsCiAgICAgICAgICAiU0MxMF9kYXk4IikKZm9yKGkgaW4gMTpsZW5ndGgoQWNvbCkpewogIGogPSAyK2kKICBrID0gMiszKmkKICBHRV9kYXRhWyxBY29sW2ldXSA9IHJvd01lYW5zKEdFX2RhdGFbLGMoajprKV0pCn0KCgojQ2FsY3VsYXRpbmcgZm9sZCBjaGFuZ2VzIGFjcm9zcyBjb25kaXRpb25zIGluIGEgdHJpYW5ndWxhciBtYXRyaXggZm9ybQpHRV9tZWFuID0gR0VfZGF0YVssYygxLDIsMzA6MzkpXQpERVByb2MgPSBHRV9tZWFuCnN0YXJ0Y29sID0gNAplbmRjb2wgPSAxMgoKYWxsRkMgPC0gZnVuY3Rpb24oREVQcm9jLHN0YXJ0Y29sLGVuZGNvbCl7IAogIEdFX2ZvbGQgPSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjID0gY29sbmFtZXMoREVQcm9jKVtzdGFydGNvbDplbmRjb2xdCiAgCiAgI0xhc3QgaW5kZXggaXMgYSBzZWxmIGNvbXBhcmlzb24gYW5kIGlzIHJlbW92ZWQKICBmb3IoayBpbiAxOihsZW5ndGgoY29sdmVjKS0xKSl7CiAgICAjU3RhcnQgd2l0aCBjb2x1bW4gdGhhdCBpcyAxIGF3YXkgZnJvbSBpbmRleCAKICAgIGZvcihqIGluIChrKzEpOmxlbmd0aChjb2x2ZWMpKXsKICAgICAgY29tcG5hbSA9IHBhc3RlMChjb2x2ZWNbal0sIi8iLGNvbHZlY1trXSkKICAgICAgI0xvb3AgdGhyb3VnaCBlYWNoIGdlbmUvcm93ICAKICAgICAgZm9yKGkgaW4gMTpucm93KERFUHJvYykpewogICAgICAgIGYgPSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA9IERFUHJvY1tpLGNvbHZlY1trXV0KICAgICAgICAKICAgICAgICAjQ2FwdHVyZSB1cHJlZ3VsYXRpb24gYW5kIGRvd24gcmVndWxhdGlvbgogICAgICAgIGlmKGY+aCl7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAyXihmLWgpCiAgICAgICAgfWVsc2V7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAtMl4oaC1mKQogICAgICAgIH0KICAgICAgICAKICAgICAgfQogICAgfQogIH0KICAKICByZXR1cm4oR0VfZm9sZCkKICAKfQoKI1N1YnNldCBnZW5lLCB0aGVuIHBsb3QsIHRoZW4gc2F2ZSBwbG90CiNQZXJoYXBzIG1ha2UgaGVhdG1hcHMgd2l0aCBzY2FsZWQgeiBzY29yZXMKI0lzIHRoZXJlIGEgd2F5IHRvIGNvbnNvbGlkYXRlIHJlcGxpY2F0ZSB6IHNjb3Jlcz8gR2VvbWV0cmljIG1lYW4/IAojUmVndWxhciBtZWFuLCB0aGVuIHNjYWxlLgoKIyBJbXBSYXQgPSBjb2xuYW1lcyhHRV9mb2xkKVtjKDQsNSw2LDksMTIsMTQsMTcsMjEsMjQsMjUsMjYsMjcsMzAsMzIsMzYsMzcsMzgsMzkpXQoKI0xpc3Rpbmcgb2YgYWxsIGltcG9ydGFudCBjb21wYXJpc29ucz8KSW1wUmF0ID0gYygiU0MwMV9kYXkzL1NDMDFfZGF5MCIsICJTQzAxX2RheTgvU0MwMV9kYXkzIiwgIlNDMDFfZGF5OC9TQzAxX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkzL1NDMDdfZGF5MCIsICJTQzA3X2RheTgvU0MwN19kYXkzIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAKICAgICAgICAgICAiU0MxMF9kYXkzL1NDMTBfZGF5MCIsICJTQzEwX2RheTgvU0MxMF9kYXkzIiwgIlNDMTBfZGF5OC9TQzEwX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiLAogICAgICAgICAgICJTQzA3X2RheTMvU0MwMV9kYXkzIiwgIlNDMTBfZGF5My9TQzAxX2RheTMiLCAiU0MxMF9kYXkzL1NDMDdfZGF5MyIsCiAgICAgICAgICAgIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IiApCkltcF9mb2xkID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBJbXBSYXQpXQpJbXBfZm9sZDIgPSBJbXBfZm9sZFtyb3dTdW1zKGFicyhJbXBfZm9sZFssNDoyMV0pPj0xLjUpPj0xLF0KCiMgd3JpdGUudGFibGUoSW1wX2ZvbGQsIlNDMSw3LDEwLVRpbWVjb3Vyc2VQTFgtSW1wb3J0YW50RkNfMjAxODA3MjIudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GKQoKSW1wX2ZvbGQgPSByZWFkLmRlbGltKCJTQzEsNywxMC1UaW1lY291cnNlUExYLUltcG9ydGFudEZDXzIwMTgwNzIyLnR4dCIsIHNlcD0iXHQiKQoKI1N1YnNldCB0aGUgTEYgbWVhbiBvZiBpbXBvcnRhbnQgZ2VuZXMgZnJvbSBMb2cyIEZvbGQgQ2hhbmdlIChMRkMpIGNvbXBhcmlzb24gZGF0YSBmcmFtZS4KR0VfSW1wID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lSW1wX2ZvbGQyJGVuc2VtYmxfZ2VuZV9pZCkKCk5lY3JvID0gcmVhZC5kZWxpbSgiS0VHR05lY3JvcHRvc2lzX2hzYTA0MjE3XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKTmVjcm8gPSBOZWNyb1tyb3dTdW1zKGlzLm5hKE5lY3JvKSkgPT0gMCwgXQpERV9OZWNybyA9IG1lcmdlKEdFX0ltcCwgTmVjcm8sIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX05lY3JvKSA9IG1ha2UubmFtZXMoREVfTmVjcm9bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChERV9OZWNyb1szOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFKQojIHdyaXRlLnRhYmxlKERFX05lY3JvLCAiS0VHR05lY3JvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCgpBcG9wID0gcmVhZC5kZWxpbSgiS0VHR0Fwb3B0b3Npc19oc2EwNDIxMF8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkFwb3AgPSBBcG9wW3Jvd1N1bXMoaXMubmEoQXBvcCkpID09IDAsIF0KREVfQXBvcCA9IG1lcmdlKEdFX0ltcCksIEFwb3AsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0Fwb3ApID0gbWFrZS5uYW1lcyhERV9BcG9wWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfQXBvcFszOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFLCBzY2FsZSA9ICJyb3ciKQojIHdyaXRlLnRhYmxlKERFX0Fwb3AsICJLRUdHQXBvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCkZlcnIgPSByZWFkLmRlbGltKCJLRUdHRmVycm9wdG9zaXNfaHNhMDQyMTZfMDYtMjUtMTgudHh0IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpGZXJyID0gRmVycltyb3dTdW1zKGlzLm5hKEZlcnIpKSA9PSAwLCBdCkRFX0ZlcnIgPSBtZXJnZShHRV9JbXAsIEZlcnIsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0ZlcnIpID0gbWFrZS5uYW1lcyhERV9GZXJyWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfRmVycls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCiMgd3JpdGUudGFibGUoREVfRmVyciwgIktFR0dGZXJyb3B0b3NpcyBTQzEsNywxMCBERVNlcSBMUlQudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpCgoKYGBgCgojIyMjIDQuIERpZmZlcmVudCBMQyBjb21wYXJpc29ucy4gQmV0d2VlbiBzdWJjbG9uZXMgYW5kIGF0IGJhc2VsaW5lIHZzIGlkbGluZy4KWnNjb3JlIGhlYXRtYXBzIG9mIHJlbGV2YW50IGNvbXBhcmlzb25zIGNhbiBiZSBtYWRlIGFzIGluIGFib3ZlIHRvIHZpc3VhbGl6ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CgojVVNFUyBBQk9WRSBDT0RFIFRPIExJTkUgMjgwLiBSdW4gdGhhdCBwc2V1ZG8tZnVuY3Rpb24uCgojIGxpYnJhcnkocGhlYXRtYXApCiNDb21wYXJpc29ucyBvZiBkaWZFeCBiZXR3ZWVuIHN1YmNsb25lcyBhdCBiYXNlbGluZSBhbmQgaWRsaW5nCkJldHdlZW5CYXNlID0gYygiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiKQpCZXR3ZWVuSWRsZSA9IGMoIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IikKIAoKI1Vuc3VyZSBvZiBob3cgc3RyaWN0IHRvIG1ha2UgdGhlIGN1dG9mZi4gU2hvdWxkIGFsbCB0aGUgZ2VuZXMgYmV0d2VlbiBjbG9uZXMgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkICgzKSBvciBpcyBhIHNpbmdsZSBkaWZmZXJlbmNlIHN1ZmZpY2llbnQ/CkJ0d19iID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBCZXR3ZWVuQmFzZSldCkJ0d19iMSA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MSxdCkJ0d19iMiA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MixdCkJ0d19iMyA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MyxdCgpCdHdfaSA9IEdFX2ZvbGRbLGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoZ25jX3N5bWJvbCIsICJwYWRqIiwgQmV0d2VlbklkbGUpXQpCdHdfaTEgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTEsXQpCdHdfaTIgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTIsXQpCdHdfaTMgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTMsXQoKI1RoaXMgZG9lcyBub3QgYWNjb3VudCBmb3Igc2FtZSBkaXJlY3Rpb24gb2YgY2hhbmdlLiBUaGlzIGNhbiBiZSBwbG90dGVkIGluIGEgaGVhdG1hcCB0byB2aWV3LgojTWVtYmVycyB0aGF0IHdlcmUgImxvc3QiIGJ5IHRoZSBiYXNlbGluZSBjb25kaXRpb24gYXQgYmVpbmcgZGlmZmVyZW50LiBXZXJlIG5vIGxvbmdlciBmb3VuZCBkaWZmRXggYmV0d2VlbiB0aGUgc3ViY2xvbmVzIHdoZW4gY29tcGFyaW5nIGJhc2VsaW5lIHRvIGlkbGluZyBERUdzLgpMb3N0REVHX2JfMSA9IHN1YnNldChCdHdfYjEsIUJ0d19iMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kxJGVuc2VtYmxfZ2VuZV9pZCkKTG9zdERFR19iXzIgPSBzdWJzZXQoQnR3X2IyLCFCdHdfYjIkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMiRlbnNlbWJsX2dlbmVfaWQpCkxvc3RERUdfYl8zID0gc3Vic2V0KEJ0d19iMywgIUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjTWFrZSBoZWF0bWFwCkxvc3RERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMyRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfM19tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8zX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKI01lbWJlcnMgdGhhdCByZW1haW5lZCBkaWZmZXJlbnQuIApTdGF0aWNERUdfYl8xID0gc3Vic2V0KEJ0d19iMSxCdHdfYjEkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQpClN0YXRpY0RFR19iXzIgPSBzdWJzZXQoQnR3X2IyLEJ0d19iMiRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCkKU3RhdGljREVHX2JfMyA9IHN1YnNldChCdHdfYjMsIEJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjU29tZSBIR05DX3N5bWJvbHMgYXJlIGR1cGxpY2F0ZXMhIEkgc3dpdGNoZWQgdG8gZW5zZW1ibF9nZW5lX2lkIHRvIGZpeC4KU3RhdGljREVHX2lfMyA9IHN1YnNldChCdHdfaTMsIEJ0d19pMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IzJGVuc2VtYmxfZ2VuZV9pZCkKCgojI01ha2UgaGVhdG1hcApTdGF0aWNERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVTdGF0aWNERUdfYl8zJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhbikgPSBtYWtlLm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKFN0YXRpY0RFR19iXzNfbWVhbls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgoKI01lbWJlcnMgdGhhdCAiZ2FpbmVkIiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzdWJjbG9uZXMgaW4gaWRsaW5nLiAgCkdhaW5ERUdfaV8xID0gc3Vic2V0KEJ0d19pMSwgIUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IxJGVuc2VtYmxfZ2VuZV9pZCkKR2FpbkRFR19pXzIgPSBzdWJzZXQoQnR3X2kyLCAhQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCVpbiVCdHdfYjIkZW5zZW1ibF9nZW5lX2lkKQpHYWluREVHX2lfMyA9IHN1YnNldChCdHdfaTMsICFCdHdfaTMkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQpCgojI01ha2UgaGVhdG1hcApHYWluREVHX2lfM19tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lR2FpbkRFR19pXzMkZW5zZW1ibF9nZW5lX2lkKQpyb3cubmFtZXMoR2FpbkRFR19pXzNfbWVhbikgPSBtYWtlLm5hbWVzKEdhaW5ERUdfaV8zX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChHYWluREVHX2lfM19tZWFuWzQ6MTJdLGNsdXN0ZXJfY29scz1GQUxTRSwgc2NhbGUgPSAicm93IikKCgojTWVtYmVycyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gaWRsaW5nICg4ZGF5KSBhbmQgYmFzZWxpbmUgd2l0aGluIHN1YmNsb25lcy4gVGhvc2Ugd2l0aCBzaGFyZWQgZGlmZkV4IG1heSBiZSBjb252ZXJnZW50IGFjcm9zcyBtdWx0aXBsZSBzdWJjbG9uZXMgZGVwZW5kaW5nIG9uIGRpcmVjdGlvbiBvZiBleHByZXNpc29uIGNoYW5nZS4KRW5kcG9pbnQgPSBjKCJTQzAxX2RheTgvU0MwMV9kYXkwIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAiU0MxMF9kYXk4L1NDMTBfZGF5MCIpCkJ0b0lkbGUgPSBHRV9mb2xkWyxjKCJlbnNlbWJsX2dlbmVfaWQiLCAiaGduY19zeW1ib2wiLCAicGFkaiIsIEVuZHBvaW50KV0KQnRvSWRsZV8xID0gQnRvSWRsZVtyb3dTdW1zKGFicyhCdG9JZGxlWyw0OjZdKT49MS41KT49MSxdCkJ0b0lkbGVfMiA9IEJ0b0lkbGVbcm93U3VtcyhhYnMoQnRvSWRsZVssNDo2XSk+PTEuNSk+PTIsXQpCdG9JZGxlXzMgPSBCdG9JZGxlW3Jvd1N1bXMoYWJzKEJ0b0lkbGVbLDQ6Nl0pPj0xLjUpPj0zLF0KCiMjTWFrZSBoZWF0bWFwCkJ0b0lkbGVfMl9tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lQnRvSWRsZV8yJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuKSA9IG1ha2UubmFtZXMoQnRvSWRsZV8yX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQoKQnRvSWRsZV8yX21lYW5faW5jRXhwID0gQnRvSWRsZV8yX21lYW5bd2hpY2goQnRvSWRsZV8yX21lYW4kU0MwMV9kYXkwIDwgQnRvSWRsZV8yX21lYW4kU0MwMV9kYXk4KSxdCkJ0b0lkbGVfMl9tZWFuX2luY0V4cCA9IEJ0b0lkbGVfMl9tZWFuX2luY0V4cFt3aGljaChCdG9JZGxlXzJfbWVhbl9pbmNFeHAkU0MwN19kYXkwIDwgQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMDdfZGF5OCksXQpCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2goQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMTBfZGF5MCA8IEJ0b0lkbGVfMl9tZWFuX2luY0V4cCRTQzEwX2RheTgpLF0KCkxvc3RERUdfYl8yX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMiRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfMl9tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzJfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8yX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKQnRvSWRsZUluY0V4cF9ERWJldHdlZW5TQ3MgPSBCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2gocm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuX2luY0V4cCkgJWluJSByb3cubmFtZXMoTG9zdERFR19iXzJfbWVhbikpLF0KCnBoZWF0bWFwKEJ0b0lkbGVfMl9tZWFuX2luY0V4cFs0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgojIGxpYnJhcnkoZGV2dG9vbHMpCiMgIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKIyBsaWJyYXJ5KGVucmljaFIpCmRicyA8LSBsaXN0RW5yaWNockRicygpCmhlYWQoZGJzKQpkYnMgPC0gYygiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiLCAiR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiLCAiR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiKQoKZW5yaWNoZWQgPC0gZW5yaWNocihyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSwgZGJzKQoKVmlldyhlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dKQpWaWV3KGVucmljaGVkW1siR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiXV0pClZpZXcoZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSkKCmVucmljaGVkX01GX3NpZyA8LSBlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dW2VucmljaGVkW1siR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiXV0kQWRqdXN0ZWQuUC52YWx1ZTwwLjA1LF0KZW5yaWNoZWRfTUZfc2lnX2RmIDwtIGRhdGEuZnJhbWUoZW5yaWNoZWRfTUZfc2lnWyxjKDEsNCw5KV0pCndyaXRlLmNzdihlbnJpY2hlZF9NRl9zaWdfZGYsICJlbnJpY2hlZF9NRl9zaWduaWZpY2FudC5jc3YiKQoKZW5yaWNoZWRfQlBfc2lnIDwtIGVucmljaGVkW1siR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiXV1bZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSRBZGp1c3RlZC5QLnZhbHVlPDAuMDUsXQplbnJpY2hlZF9CUF9zaWdfZGYgPC0gZGF0YS5mcmFtZShlbnJpY2hlZF9CUF9zaWdbLGMoMSw0LDkpXSkKIyB3cml0ZS5jc3YoZW5yaWNoZWRfQlBfc2lnX2RmLCAiZW5yaWNoZWRfQlBfc2lnbmlmaWNhbnQuY3N2IikKCkdpbmlfc2NHZW5lcyA8LSBjKCJBUE9FIiwgIkJDQU4iLCAiQ0VTMSIsICJDSVRFRDEiLAogICAgICAgICAgICAgICAgICAiQ1BNIiwgIkNUU0YiLCAiRENUIiwgIkVETlJCIiwgCiAgICAgICAgICAgICAgICAgICJFR1IxIiwgIkVTUlAxIiwgIkZTVEwxIiwgIk1BTEFUMSIsCiAgICAgICAgICAgICAgICAgICJNQVAySzYiLCAiTUNGMkwiLCAiTUxBTkEiLCAiTVhENCIsCiAgICAgICAgICAgICAgICAgICJPQ0EyIiwgIlBNRUwiLCAiU0VNQTZBIiwgIlNOQUkyIiwKICAgICAgICAgICAgICAgICAgIlNPWDQiLCAiVFNQQU4xMCIpCmVucmljaGVkX3NjIDwtIGVucmljaHIoR2luaV9zY0dlbmVzLCBkYnMpCgpyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSAlaW4lIEdpbmlfc2NHZW5lcwoKYGBgCgoKCiMjIyMgUmVzdCBvZiBKYWNrJ3MgQW5hbHlzaXMgIyMjIwpgYGB7ciBldmFsPUZBTFNFfQojVmlzdWFsbHkgaW5zcGVjdCB0cmVuZGluZyBtZW1iZXJzIGZyb20gaGVhdG1hcHMuCiNQbG90cyBvZiBzcGVjaWZpYyB0cmVuZGluZyBtZW1iZXJzPwpwIDwtIGdncGxvdChkYXRhPWRmMiwgYWVzKHg9ZG9zZSwgeT1sZW4sIGZpbGw9c3VwcCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBOT1RFOiBjb2RlIGJlbG93IHJldXNlcyBvYmplY3QgbmFtZXMuLi4gV0lMTCBPVkVSV1JJVEUhCmBgYHtyIGV2YWw9RkFMU0V9CiNHTE0gQ29lZiBIZWF0bWFwLgpiZXRhcyA8LSBjb2VmKGRkcykKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCm1hdCA8LSBkYXRhLmZyYW1lKGJldGFzW3RvcEdlbmVzLF0pCm1hdCRlbnNlbWJsX2dlbmVfaWQgPSByb3cubmFtZXMobWF0KQptYXQkcGFkaiA9IHJlc1t0b3BHZW5lcywicGFkaiJdCiMgZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKIyBtYXJ0IDwtIHVzZURhdGFzZXQoImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIsIG1hcnQgPSBlbnNlbWJsKQojIGdlbmVzID0gcm93Lm5hbWVzKG1hdCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKIyBHRV9kYXRhIDwtIG1lcmdlKG1hdCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQojIHJvd25hbWVzKEdFX2RhdGEpIDwtIG1ha2UubmFtZXMoR0VfZGF0YVssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCiMgR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI1NvcnRpbmcgc2NyaXB0IHRvIHBpY2sgb3V0IGVudHJpZXMgZ3JlYXRlciB0aGFuIG9yIGxlc3MgdGhhbiArLTEKZWcgPSBjKCkKZm9yKGkgaW4gMzoxMCl7CiAgZyA9IHdoaWNoKEdFX2RhdGFbLGldID4gMyB8IEdFX2RhdGFbLGldIDwgLTMpCiAgZWcgPSBjKGVnLGcpCn0KZWcgPSB1bmlxdWUoZWcpCgptYXQgPSBHRV9kYXRhW2VnLC1jKDE6MiwxMSwxMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKIyBsaWJyYXJ5KHBoZWF0bWFwKQpwaGVhdG1hcChtYXQsIGNsdXN0ZXJfY29scyA9IEZBTFNFKQoKIyBzc2RnID0gc2RnWzE6MTAwMCwgXQpkaW0oc2RnKQpoZWFkKHNkZykKCmBgYAoKCg==